From eab82a51e3c2514ff73c523f6e9ad24eb90e6b7f Mon Sep 17 00:00:00 2001 From: Vector-Hector Date: Fri, 22 Dec 2023 18:04:13 +0100 Subject: [PATCH 1/2] add bson support --- go.mod | 2 + go.sum | 41 +++++++++++++++++++++ journey.go | 88 +++++++++++++++++++++++++++++--------------- line.go | 53 +++++++++++++++++++++----- location.go | 36 +++++++++++++----- object-type.go | 24 ++++++------ operator.go | 44 +++++++++++++++++++--- region.go | 47 +++++++++++++++++++---- route.go | 37 ++++++++++++++----- schedule.go | 54 +++++++++++++++++++++------ station.go | 47 +++++++++++++++++++---- stop-station.go | 54 ++++++++++++++++++++++++++- stop.go | 44 ++++++++++++++++++---- stopover.go | 38 +++++++++++++------ test/deepequal.go | 50 +++++++++++++++++++++++-- test/journey_test.go | 70 ++++++++++++++++++++++++++++++----- time.go | 32 +++++++++++++++- 17 files changed, 628 insertions(+), 133 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index 1f5e925..d06b4bf 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/Vector-Hector/fptf go 1.15 + +require go.mongodb.org/mongo-driver v1.13.1 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..74b4fbb --- /dev/null +++ b/go.sum @@ -0,0 +1,41 @@ +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= +go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/journey.go b/journey.go index 6915fa1..26a08f9 100644 --- a/journey.go +++ b/journey.go @@ -2,6 +2,8 @@ package fptf import ( "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" "time" ) @@ -95,41 +97,41 @@ func (j *Journey) GetArrivalDelay() *int { // Trip is a formalized, inferred version of a journey leg type Trip struct { - Origin *StopStation `json:"origin,omitempty"` - Destination *StopStation `json:"destination,omitempty"` + Origin *StopStation `json:"origin,omitempty" bson:"origin,omitempty"` + Destination *StopStation `json:"destination,omitempty" bson:"destination,omitempty"` - Departure TimeNullable `json:"departure,omitempty"` - DepartureDelay *int `json:"departureDelay,omitempty"` - DeparturePlatform string `json:"departurePlatform,omitempty"` + Departure TimeNullable `json:"departure,omitempty" bson:"departure,omitempty"` + DepartureDelay *int `json:"departureDelay,omitempty" bson:"departureDelay,omitempty"` + DeparturePlatform string `json:"departurePlatform,omitempty" bson:"departurePlatform,omitempty"` - Arrival TimeNullable `json:"arrival,omitempty"` - ArrivalDelay *int `json:"arrivalDelay,omitempty"` - ArrivalPlatform string `json:"arrivalPlatform,omitempty"` + Arrival TimeNullable `json:"arrival,omitempty" bson:"arrival,omitempty"` + ArrivalDelay *int `json:"arrivalDelay,omitempty" bson:"arrivalDelay,omitempty"` + ArrivalPlatform string `json:"arrivalPlatform,omitempty" bson:"arrivalPlatform,omitempty"` - Schedule *Schedule `json:"schedule,omitempty"` + Schedule *Schedule `json:"schedule,omitempty" bson:"schedule,omitempty"` - Stopovers []*Stopover `json:"stopovers,omitempty"` + Stopovers []*Stopover `json:"stopovers,omitempty" bson:"stopovers,omitempty"` - Mode Mode `json:"mode,omitempty"` - SubMode string `json:"subMode,omitempty"` + Mode Mode `json:"mode,omitempty" bson:"mode,omitempty"` + SubMode string `json:"subMode,omitempty" bson:"subMode,omitempty"` - Public *bool `json:"public,omitempty"` + Public *bool `json:"public,omitempty" bson:"public,omitempty"` - Operator *Operator `json:"operator,omitempty"` + Operator *Operator `json:"operator,omitempty" bson:"operator,omitempty"` - Price *Price `json:"price,omitempty"` + Price *Price `json:"price,omitempty" bson:"price,omitempty"` // Some additional arguments, inspired by https://github.com/public-transport/hafas-client - Line *Line `json:"line,omitempty"` // The line on which this trip is going - Direction string `json:"direction,omitempty"` // The direction string on the train + Line *Line `json:"line,omitempty" bson:"line,omitempty"` // The line on which this trip is going + Direction string `json:"direction,omitempty" bson:"direction,omitempty"` // The direction string on the train - Meta interface{} `json:"meta,omitempty"` // any additional data + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` // any additional data } type Price struct { - Amount float64 `json:"amount,omitempty"` - Currency string `json:"currency,omitempty"` - Meta interface{} `json:"meta,omitempty"` // any additional data + Amount float64 `json:"amount,omitempty" bson:"amount,omitempty"` + Currency string `json:"currency,omitempty" bson:"currency,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` // any additional data } // GetMode The mode of a trip can be defined in many places. @@ -168,9 +170,9 @@ func (trip *Trip) GetLine() *Line { } func (trip *Trip) SubTrip(startInclusive int, endExclusive int) *Trip { - stopovers := trip.Stopovers[startInclusive : endExclusive] + stopovers := trip.Stopovers[startInclusive:endExclusive] origin := stopovers[0] - dest := stopovers[len(stopovers) - 1] + dest := stopovers[len(stopovers)-1] return &Trip{ Origin: origin.StopStation, @@ -195,16 +197,16 @@ func (trip *Trip) SubTrip(startInclusive int, endExclusive int) *Trip { } type mJourney struct { - typed - Id string `json:"id"` - Trips []*Trip `json:"legs"` - Price *Price `json:"price"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Trips []*Trip `json:"legs,omitempty" bson:"trips,omitempty"` + Price *Price `json:"price,omitempty" bson:"price,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (j *Journey) toM() *mJourney { return &mJourney{ - typed: typedJourney, + Typed: typedJourney, Id: j.Id, Trips: j.Trips, Price: j.Price, @@ -235,3 +237,31 @@ func (j *Journey) UnmarshalJSON(data []byte) error { func (j *Journey) MarshalJSON() ([]byte, error) { return json.Marshal(j.toM()) } + +func (j *Journey) UnmarshalBSON(data []byte) error { + var m mJourney + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + j.fromM(&m) + return nil +} + +func (j *Journey) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + var m mJourney + err := bson.UnmarshalValue(typ, data, &m) + if err != nil { + return err + } + j.fromM(&m) + return nil +} + +func (j Journey) MarshalBSON() ([]byte, error) { + return bson.Marshal(j.toM()) +} + +func (j Journey) MarshalBSONValue() (bsontype.Type, []byte, error) { + return bson.MarshalValue(j.toM()) +} diff --git a/line.go b/line.go index 255821d..b1409a9 100644 --- a/line.go +++ b/line.go @@ -1,6 +1,10 @@ package fptf -import "encoding/json" +import ( + "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" +) type Line struct { Id string @@ -15,19 +19,19 @@ type Line struct { } type mLine struct { - typed - Id string `json:"id"` - Name string `json:"name"` - Mode Mode `json:"mode"` - SubMode string `json:"subMode,omitempty"` - Routes []*Route `json:"routes,omitempty"` - Operator *Operator `json:"operator"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Mode Mode `json:"mode,omitempty" bson:"mode,omitempty"` + SubMode string `json:"subMode,omitempty" bson:"subMode,omitempty"` + Routes []*Route `json:"routes,omitempty" bson:"routes,omitempty"` + Operator *Operator `json:"operator,omitempty" bson:"operator,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (w *Line) toM() *mLine { return &mLine{ - typed: typedLine, + Typed: typedLine, Id: w.Id, Name: w.Name, Mode: w.Mode, @@ -73,3 +77,32 @@ func (w *Line) MarshalJSON() ([]byte, error) { } return json.Marshal(w.toM()) } + +func (w *Line) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeEmbeddedDocument { + var m mLine + err := bson.UnmarshalValue(bson.TypeEmbeddedDocument, data, &m) + if err != nil { + return err + } + w.Partial = false + w.fromM(&m) + return nil + } + + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + w.Id = id + w.Partial = true + return nil +} + +func (w Line) MarshalBSONValue() (bsontype.Type, []byte, error) { + if w.Partial { + return bson.MarshalValue(w.Id) + } + return bson.MarshalValue(w.toM()) +} diff --git a/location.go b/location.go index 3190b86..aa38f54 100644 --- a/location.go +++ b/location.go @@ -1,6 +1,10 @@ package fptf -import "encoding/json" +import ( + "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" +) type Location struct { Name string @@ -12,18 +16,18 @@ type Location struct { } type mLocation struct { - typed - Name string `json:"name,omitempty"` - Address string `json:"address,omitempty"` - Longitude float64 `json:"longitude,omitempty"` - Latitude float64 `json:"latitude,omitempty"` - Altitude float64 `json:"altitude,omitempty"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Address string `json:"address,omitempty" bson:"address,omitempty"` + Longitude float64 `json:"longitude,omitempty" bson:"longitude,omitempty"` + Latitude float64 `json:"latitude,omitempty" bson:"latitude,omitempty"` + Altitude float64 `json:"altitude,omitempty" bson:"altitude,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (w *Location) toM() *mLocation { return &mLocation{ - typed: typedLocation, + Typed: typedLocation, Name: w.Name, Address: w.Address, Longitude: w.Longitude, @@ -55,3 +59,17 @@ func (w *Location) UnmarshalJSON(data []byte) error { func (w *Location) MarshalJSON() ([]byte, error) { return json.Marshal(w.toM()) } + +func (w *Location) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + var m mLocation + err := bson.UnmarshalValue(typ, data, &m) + if err != nil { + return err + } + w.fromM(&m) + return nil +} + +func (w Location) MarshalBSONValue() (bsontype.Type, []byte, error) { + return bson.MarshalValue(w.toM()) +} diff --git a/object-type.go b/object-type.go index a1dadb3..2aff9ba 100644 --- a/object-type.go +++ b/object-type.go @@ -16,19 +16,19 @@ const ( objectTypeJourney objectType = "journey" ) -type typed struct { - Type objectType `json:"type"` +type Typed struct { + Type objectType `json:"type,omitempty" bson:"type,omitempty"` } var ( - typedLocation = typed{Type: objectTypeLocation} - typedStation = typed{Type: objectTypeStation} - typedStop = typed{Type: objectTypeStop} - typedRegion = typed{Type: objectTypeRegion} - typedLine = typed{Type: objectTypeLine} - typedRoute = typed{Type: objectTypeRoute} - typedSchedule = typed{Type: objectTypeSchedule} - typedOperator = typed{Type: objectTypeOperator} - typedStopover = typed{Type: objectTypeStopover} - typedJourney = typed{Type: objectTypeJourney} + typedLocation = Typed{Type: objectTypeLocation} + typedStation = Typed{Type: objectTypeStation} + typedStop = Typed{Type: objectTypeStop} + typedRegion = Typed{Type: objectTypeRegion} + typedLine = Typed{Type: objectTypeLine} + typedRoute = Typed{Type: objectTypeRoute} + typedSchedule = Typed{Type: objectTypeSchedule} + typedOperator = Typed{Type: objectTypeOperator} + typedStopover = Typed{Type: objectTypeStopover} + typedJourney = Typed{Type: objectTypeJourney} ) diff --git a/operator.go b/operator.go index 76c04f7..e1aac93 100644 --- a/operator.go +++ b/operator.go @@ -1,6 +1,10 @@ package fptf -import "encoding/json" +import ( + "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" +) type Operator struct { Id string @@ -12,18 +16,18 @@ type Operator struct { // intermediate format used by marshal. this is to work out the partial and type part. type mOperator struct { - Id string `json:"id"` - Name string `json:"name"` - Meta interface{} `json:"meta,omitempty"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` - typed + Typed `bson:"inline"` } func (o *Operator) toM() *mOperator { return &mOperator{ + Typed: typedOperator, Id: o.Id, Name: o.Name, - typed: typedOperator, Meta: o.Meta, } } @@ -60,3 +64,31 @@ func (o *Operator) MarshalJSON() ([]byte, error) { } return json.Marshal(o.toM()) } + +func (o *Operator) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeString { + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + o.Id = id + o.Partial = true + return nil + } + o.Partial = false + var m mOperator + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + o.fromM(&m) + return nil +} + +func (o Operator) MarshalBSONValue() (bsontype.Type, []byte, error) { + if o.Partial { + return bson.MarshalValue(o.Id) + } + return bson.MarshalValue(o.toM()) +} diff --git a/region.go b/region.go index 2c7f27e..f771042 100644 --- a/region.go +++ b/region.go @@ -1,6 +1,10 @@ package fptf -import "encoding/json" +import ( + "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" +) // A Region is a group of Stations, for example a metropolitan area // or a geographical or cultural region. @@ -22,16 +26,16 @@ type Region struct { } type mRegion struct { - typed - Id string `json:"id"` - Name string `json:"name"` - Stations []*Station `json:"stations"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Stations []*Station `json:"stations,omitempty" bson:"stations,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (w *Region) toM() *mRegion { return &mRegion{ - typed: typedRegion, + Typed: typedRegion, Id: w.Id, Name: w.Name, Stations: w.Stations, @@ -71,3 +75,32 @@ func (w *Region) MarshalJSON() ([]byte, error) { } return json.Marshal(w.toM()) } + +func (w *Region) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeString { + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + w.Id = id + w.Partial = true + return nil + } + + w.Partial = false + var m mRegion + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + w.fromM(&m) + return nil +} + +func (w Region) MarshalBSONValue() (bsontype.Type, []byte, error) { + if w.Partial { + return bson.MarshalValue(w.Id) + } + return bson.MarshalValue(w.toM()) +} diff --git a/route.go b/route.go index 83f4bf3..54c8fe1 100644 --- a/route.go +++ b/route.go @@ -1,6 +1,10 @@ package fptf -import "encoding/json" +import ( + "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" +) // A Route represents a single set of stations, of a single Line. // @@ -18,18 +22,18 @@ type Route struct { // used by marshal type mRoute struct { - typed - Id string `json:"id"` - Line *Line `json:"line"` - Mode Mode `json:"mode,omitempty"` - SubMode string `json:"subMode,omitempty"` - Stops []*Stop `json:"stops"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Line *Line `json:"line,omitempty" bson:"line,omitempty"` + Mode Mode `json:"mode,omitempty" bson:"mode,omitempty"` + SubMode string `json:"subMode,omitempty" bson:"subMode,omitempty"` + Stops []*Stop `json:"stops,omitempty" bson:"stops,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (w *Route) toM() *mRoute { return &mRoute{ - typed: typedRoute, + Typed: typedRoute, Id: w.Id, Line: w.Line, Mode: w.Mode, @@ -61,3 +65,18 @@ func (w *Route) UnmarshalJSON(data []byte) error { func (w *Route) MarshalJSON() ([]byte, error) { return json.Marshal(w.toM()) } + +func (w *Route) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + var m mRoute + err := bson.UnmarshalValue(typ, data, &m) + if err != nil { + return err + } + + w.fromM(&m) + return nil +} + +func (w Route) MarshalBSONValue() (bsontype.Type, []byte, error) { + return bson.MarshalValue(w.toM()) +} diff --git a/schedule.go b/schedule.go index 2e5f59e..77e41fe 100644 --- a/schedule.go +++ b/schedule.go @@ -2,6 +2,8 @@ package fptf import ( "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" ) // Schedule There are many ways to format schedules of public transport @@ -21,25 +23,25 @@ type Schedule struct { } type SequenceElement struct { - Arrival int64 `json:"arrival"` - Departure int64 `json:"departure"` + Arrival *int64 `json:"arrival,omitempty" bson:"arrival,omitempty"` + Departure *int64 `json:"departure,omitempty" bson:"departure,omitempty"` } // used by marshal type mSchedule struct { - typed - Id string `json:"id"` - Route *Route `json:"route"` - Mode Mode `json:"mode,omitempty"` - SubMode string `json:"subMode,omitempty"` - Sequence []*SequenceElement `json:"sequence"` - Starts []TimeUnix `json:"starts"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Route *Route `json:"route,omitempty" bson:"route,omitempty"` + Mode Mode `json:"mode,omitempty" bson:"mode,omitempty"` + SubMode string `json:"subMode,omitempty" bson:"subMode,omitempty"` + Sequence []*SequenceElement `json:"sequence,omitempty" bson:"sequence,omitempty"` + Starts []TimeUnix `json:"starts,omitempty" bson:"starts,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (w *Schedule) toM() *mSchedule { return &mSchedule{ - typed: typedSchedule, + Typed: typedSchedule, Id: w.Id, Route: w.Route, Mode: w.Mode, @@ -86,3 +88,33 @@ func (w *Schedule) MarshalJSON() ([]byte, error) { } return json.Marshal(w.toM()) } + +func (w *Schedule) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeString { + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + w.Id = id + w.Partial = true + return nil + } + + w.Partial = false + var m mSchedule + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + + w.fromM(&m) + return nil +} + +func (w Schedule) MarshalBSONValue() (bsontype.Type, []byte, error) { + if w.Partial { + return bson.MarshalValue(w.Id) + } + return bson.MarshalValue(w.toM()) +} diff --git a/station.go b/station.go index b3b5897..54e6096 100644 --- a/station.go +++ b/station.go @@ -2,6 +2,8 @@ package fptf import ( "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" ) // Station is a larger building or area that can be identified by a name. @@ -20,22 +22,22 @@ type Station struct { // used by marshal type mStation struct { - typed - Id string `json:"id"` - Name string `json:"name"` - Location *Location `json:"location,omitempty"` - Regions []*Region `json:"regions,omitempty"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Location *Location `json:"location,omitempty" bson:"location,omitempty"` + Regions []*Region `json:"regions,omitempty" bson:"regions,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (s *Station) toM() *mStation { return &mStation{ - typed: typedStation, + Typed: typedStation, Id: s.Id, Name: s.Name, Location: s.Location, Regions: s.Regions, - Meta: s.Meta, + Meta: s.Meta, } } @@ -73,3 +75,32 @@ func (s *Station) MarshalJSON() ([]byte, error) { } return json.Marshal(s.toM()) } + +func (s *Station) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeString { + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + s.Id = id + s.Partial = true + return nil + } + + s.Partial = false + var m mStation + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + s.fromM(&m) + return nil +} + +func (s Station) MarshalBSONValue() (bsontype.Type, []byte, error) { + if s.Partial { + return bson.MarshalValue(s.Id) + } + return bson.MarshalValue(s.toM()) +} diff --git a/stop-station.go b/stop-station.go index 1acf085..ed32a9f 100644 --- a/stop-station.go +++ b/stop-station.go @@ -3,6 +3,9 @@ package fptf import ( "encoding/json" "errors" + "fmt" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" ) type StopStation struct { @@ -44,7 +47,7 @@ func (s *StopStation) SetLocation(loc *Location) { } if s.Id != nil { s.Station = &Station{ - Id: *s.Id, + Id: *s.Id, Location: loc, } s.Id = nil @@ -170,3 +173,52 @@ func (s *StopStation) MarshalJSON() ([]byte, error) { } return []byte("null"), nil } + +func (s *StopStation) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeString { + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + s.Id = &id + return nil + } + + var station mStation + stationErr := bson.Unmarshal(data, &station) + if stationErr == nil && station.Type == objectTypeStation { + s.Station = new(Station) + s.Station.fromM(&station) + return nil + } + + var stop mStop + stopErr := bson.Unmarshal(data, &stop) + if stopErr == nil && stop.Type == objectTypeStop { + s.Stop = new(Stop) + s.Stop.fromM(&stop) + return nil + } + + var m bson.M + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + + return fmt.Errorf("could not unmarshall to any of type string, station or stop. stationErr: %v, stopErr: %v, data type: %v, fptf type: %s, object as map: %v", stationErr, stopErr, typ, string(stop.Type), m) +} + +func (s StopStation) MarshalBSONValue() (bsontype.Type, []byte, error) { + if s.Id != nil { + return bson.MarshalValue(s.Id) + } + if s.Station != nil { + return s.Station.MarshalBSONValue() + } + if s.Stop != nil { + return s.Stop.MarshalBSONValue() + } + return bson.MarshalValue(nil) +} diff --git a/stop.go b/stop.go index 2a98452..b01dd2b 100644 --- a/stop.go +++ b/stop.go @@ -2,6 +2,8 @@ package fptf import ( "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" ) // Stop is a single small point or structure at which vehicles stop. @@ -22,17 +24,17 @@ type Stop struct { // used by marshal type mStop struct { - typed - Id string `json:"id"` - Name string `json:"name"` - Station *Station `json:"station"` - Location *Location `json:"location,omitempty"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + Id string `json:"id,omitempty" bson:"id,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Station *Station `json:"station,omitempty" bson:"station,omitempty"` + Location *Location `json:"location,omitempty" bson:"location,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (s *Stop) toM() *mStop { return &mStop{ - typed: typedStop, + Typed: typedStop, Id: s.Id, Name: s.Name, Station: s.Station, @@ -75,3 +77,31 @@ func (s *Stop) MarshalJSON() ([]byte, error) { } return json.Marshal(s.toM()) } + +func (s *Stop) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeString { + var id string + err := bson.UnmarshalValue(bson.TypeString, data, &id) + if err != nil { + return err + } + s.Id = id + s.Partial = true + return nil + } + s.Partial = false + var m mStop + err := bson.Unmarshal(data, &m) + if err != nil { + return err + } + s.fromM(&m) + return nil +} + +func (s *Stop) MarshalBSONValue() (bsontype.Type, []byte, error) { + if s.Partial { + return bson.MarshalValue(s.Id) + } + return bson.MarshalValue(s.toM()) +} diff --git a/stopover.go b/stopover.go index 736bc0c..3df0c86 100644 --- a/stopover.go +++ b/stopover.go @@ -2,6 +2,8 @@ package fptf import ( "encoding/json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" ) // A Stopover represents a vehicle stopping at a stop/station at a specific time. @@ -16,22 +18,22 @@ type Stopover struct { Meta interface{} } -// intermediate, typed format used by marshal +// intermediate, Typed format used by marshal type mStopover struct { - typed - StopStation *StopStation `json:"stop"` - Arrival TimeNullable `json:"arrival"` - ArrivalDelay *int `json:"arrivalDelay,omitempty"` - ArrivalPlatform string `json:"arrivalPlatform,omitempty"` - Departure TimeNullable `json:"departure"` - DepartureDelay *int `json:"departureDelay,omitempty"` - DeparturePlatform string `json:"departurePlatform,omitempty"` - Meta interface{} `json:"meta,omitempty"` + Typed `bson:"inline"` + StopStation *StopStation `json:"stop,omitempty" bson:"stop,omitempty"` + Arrival TimeNullable `json:"arrival,omitempty" bson:"arrival,omitempty"` + ArrivalDelay *int `json:"arrivalDelay,omitempty" bson:"arrivalDelay,omitempty"` + ArrivalPlatform string `json:"arrivalPlatform,omitempty" bson:"arrivalPlatform,omitempty"` + Departure TimeNullable `json:"departure,omitempty" bson:"departure,omitempty"` + DepartureDelay *int `json:"departureDelay,omitempty" bson:"departureDelay,omitempty"` + DeparturePlatform string `json:"departurePlatform,omitempty" bson:"departurePlatform,omitempty"` + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` } func (s *Stopover) toM() *mStopover { return &mStopover{ - typed: typedStopover, + Typed: typedStopover, StopStation: s.StopStation, Arrival: s.Arrival, ArrivalDelay: s.ArrivalDelay, @@ -70,3 +72,17 @@ func (s *Stopover) UnmarshalJSON(data []byte) error { func (s *Stopover) MarshalJSON() ([]byte, error) { return json.Marshal(s.toM()) } + +func (s *Stopover) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + var m mStopover + err := bson.UnmarshalValue(typ, data, &m) + if err != nil { + return err + } + s.fromM(&m) + return nil +} + +func (s Stopover) MarshalBSONValue() (bsontype.Type, []byte, error) { + return bson.MarshalValue(s.toM()) +} diff --git a/test/deepequal.go b/test/deepequal.go index a87dc7c..5b5722f 100644 --- a/test/deepequal.go +++ b/test/deepequal.go @@ -1,7 +1,9 @@ package test import ( + "fmt" "reflect" + "time" ) func deepEqual(v1 interface{}, v2 interface{}) bool { @@ -12,9 +14,14 @@ func deepEqual(v1 interface{}, v2 interface{}) bool { // as we only look at json unmarshalled values, we can ignore the recursive type problem func deepValueEqual(v1 reflect.Value, v2 reflect.Value) bool { if isZero(v1) { - return isZero(v2) + bothZero := isZero(v2) + if !bothZero { + fmt.Println("Deep equal failed at value comparing values\n", v1, "\n", v2, "(v1 is zero, v2 is not)") + } + return bothZero } if isZero(v2) { + fmt.Println("Deep equal failed at value comparing values\n", v1, "\n", v2, "(v2 is zero, v1 is not)") return false } @@ -29,9 +36,11 @@ func deepValueEqual(v1 reflect.Value, v2 reflect.Value) bool { return true case reflect.Slice: if v1.IsNil() != v2.IsNil() { + fmt.Println("Deep equal failed at slice comparing values\n", v1, "\n", v2, "(one is nil, the other is not)") return false } if v1.Len() != v2.Len() { + fmt.Println("Deep equal failed at slice comparing values\n", v1, "\n", v2, "(lengths differ)") return false } if v1.Pointer() == v2.Pointer() { @@ -46,7 +55,11 @@ func deepValueEqual(v1 reflect.Value, v2 reflect.Value) bool { return true case reflect.Interface: if v1.IsNil() || v2.IsNil() { - return v1.IsNil() == v2.IsNil() + bothNil := v1.IsNil() == v2.IsNil() + if !bothNil { + fmt.Println("Deep equal failed at interface comparing values\n", v1, "\n", v2, "(one is nil, the other is not)") + } + return bothNil } return deepValueEqual(v1.Elem(), v2.Elem()) case reflect.Ptr: @@ -78,10 +91,41 @@ func deepValueEqual(v1 reflect.Value, v2 reflect.Value) bool { } return true default: - return v1.Interface() == v2.Interface() + t1, v1IsTime := getTime(v1) + t2, v2IsTime := getTime(v2) + + if v1IsTime && v2IsTime { + eq := t1.Equal(t2) + if !eq { + fmt.Println("Deep equal failed at time comparing values\n", v1, "\n", v2) + } + return eq + } + + interfacesEqual := v1.Interface() == v2.Interface() + if !interfacesEqual { + fmt.Println("Deep equal failed at default comparing values\n", v1, "\n", v2, "(interfaces not equal)") + } + return interfacesEqual } } +func getTime(val reflect.Value) (time.Time, bool) { + if val.Type().String() == "time.Time" { + return val.Interface().(time.Time), true + } + + if val.Type().String() == "string" { + t, err := time.Parse(time.RFC3339, val.Interface().(string)) + if err != nil { + return time.Time{}, false + } + return t, true + } + + return time.Time{}, false +} + func isZero(val reflect.Value) bool { if val.Kind() == reflect.Ptr { return isZero(val.Elem()) diff --git a/test/journey_test.go b/test/journey_test.go index 0746f09..1e47638 100644 --- a/test/journey_test.go +++ b/test/journey_test.go @@ -2,9 +2,10 @@ package test import ( "encoding/json" - "fmt" - "github.com/Vector-Hector/friendly-public-transport-format" + "github.com/Vector-Hector/fptf" + "go.mongodb.org/mongo-driver/bson" "io/ioutil" + "os" "testing" ) @@ -32,20 +33,24 @@ func TestParseValidSimpleJourney(t *testing.T) { func TestWriteValidJourney(t *testing.T) { testRewriteJourney(t, "valid-journey.json") + testBsonRewriteJourney(t, "valid-journey.json") } func TestWriteSimpleValidJourney(t *testing.T) { testRewriteJourney(t, "valid-simple-journey.json") + testBsonRewriteJourney(t, "valid-simple-journey.json") } func testRewriteJourney(t *testing.T, file string) { - rawDat, err := ioutil.ReadFile(file) + rawDat, err := os.ReadFile(file) if err != nil { panic(err) } journey, err := getJourneyFromBytes(rawDat) - if err != nil {t.Error(err)} + if err != nil { + t.Error(err) + } remarshalledDat, err := json.Marshal(journey) if err != nil { @@ -54,17 +59,64 @@ func testRewriteJourney(t *testing.T, file string) { var journeyRawObj interface{} err = json.Unmarshal(rawDat, &journeyRawObj) - if err != nil {t.Error(err)} + if err != nil { + t.Error(err) + } var remarshalledRawObj interface{} err = json.Unmarshal(remarshalledDat, &remarshalledRawObj) - if err != nil {t.Error(err)} + if err != nil { + t.Error(err) + } + + if !deepEqual(journeyRawObj, remarshalledRawObj) { + t.Error("JSON: marshalling the parsed data did not give the original data") + } +} + +func testBsonRewriteJourney(t *testing.T, file string) { + rawDat, err := os.ReadFile(file) + if err != nil { + panic(err) + } + + journey, err := getJourneyFromBytes(rawDat) + if err != nil { + t.Error(err) + } + + var journeyRawObj interface{} + err = json.Unmarshal(rawDat, &journeyRawObj) + if err != nil { + t.Error(err) + } + + remarshalledDat, err := bson.Marshal(journey) + if err != nil { + t.Error(err) + } + + var remarshalledJourney fptf.Journey + err = bson.Unmarshal(remarshalledDat, &remarshalledJourney) + if err != nil { + t.Error(err) + } + + remarshalledJsonData, err := json.Marshal(&remarshalledJourney) + if err != nil { + t.Error(err) + } + + var remarshalledRawObj interface{} + err = json.Unmarshal(remarshalledJsonData, &remarshalledRawObj) + if err != nil { + t.Error(err) + } - fmt.Println("Remarshalled", file, ":") - fmt.Println(string(remarshalledDat)) if !deepEqual(journeyRawObj, remarshalledRawObj) { - t.Error("Marshalling the parsed data did not give the original data") + t.Error("BSON: marshalling the parsed data did not give the original data") } + } func getJourneyFromBytes(dat []byte) (*fptf.Journey, error) { diff --git a/time.go b/time.go index 32f59a6..4979a87 100644 --- a/time.go +++ b/time.go @@ -1,6 +1,8 @@ package fptf import ( + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" "strconv" "time" ) @@ -25,6 +27,20 @@ func (t *TimeUnix) UnmarshalJSON(s []byte) error { return nil } +func (t *TimeUnix) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + var i int64 + err := bson.UnmarshalValue(typ, data, &i) + if err != nil { + return err + } + *(*time.Time)(t) = time.Unix(i, 0) + return nil +} + +func (t TimeUnix) MarshalBSONValue() (bsontype.Type, []byte, error) { + return bson.MarshalValue(time.Time(t).Unix()) +} + // Is essentially just a time.Time object, but zero values are marshalled as null type TimeNullable struct { time.Time @@ -32,8 +48,22 @@ type TimeNullable struct { // MarshalJSON is used to convert the timestamp to JSON func (t TimeNullable) MarshalJSON() ([]byte, error) { - if t == (TimeNullable{}) { + if t.IsZero() { return []byte("null"), nil } return t.Time.MarshalJSON() } + +func (t *TimeNullable) UnmarshalBSONValue(typ bsontype.Type, data []byte) error { + if typ == bson.TypeNull { + return nil + } + return bson.UnmarshalValue(typ, data, &t.Time) +} + +func (t TimeNullable) MarshalBSONValue() (bsontype.Type, []byte, error) { + if t.IsZero() { + return bson.TypeNull, nil, nil + } + return bson.MarshalValue(t.Time) +} From a728057a84e0ae5d28a08a261dc1869bbb671b3a Mon Sep 17 00:00:00 2001 From: Vector-Hector Date: Fri, 22 Dec 2023 18:34:21 +0100 Subject: [PATCH 2/2] add polyline to fptf.Trip --- journey.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/journey.go b/journey.go index 26a08f9..dd0ccf4 100644 --- a/journey.go +++ b/journey.go @@ -125,6 +125,8 @@ type Trip struct { Line *Line `json:"line,omitempty" bson:"line,omitempty"` // The line on which this trip is going Direction string `json:"direction,omitempty" bson:"direction,omitempty"` // The direction string on the train + Polyline string `json:"polyline,omitempty" bson:"polyline,omitempty"` // The polyline of the trip + Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` // any additional data } @@ -192,6 +194,7 @@ func (trip *Trip) SubTrip(startInclusive int, endExclusive int) *Trip { Price: nil, Line: trip.Line, Direction: trip.Direction, + Polyline: trip.Polyline, Meta: trip.Meta, } } @@ -199,7 +202,7 @@ func (trip *Trip) SubTrip(startInclusive int, endExclusive int) *Trip { type mJourney struct { Typed `bson:"inline"` Id string `json:"id,omitempty" bson:"id,omitempty"` - Trips []*Trip `json:"legs,omitempty" bson:"trips,omitempty"` + Trips []*Trip `json:"legs,omitempty" bson:"legs,omitempty"` Price *Price `json:"price,omitempty" bson:"price,omitempty"` Meta interface{} `json:"meta,omitempty" bson:"meta,omitempty"` }