package cursortoken import ( "encoding/base32" "encoding/json" "go.mongodb.org/mongo-driver/bson/primitive" "time" ) type CTKeySort struct { Mode Mode ValuePrimary string ValueSecondary string Direction SortDirection DirectionSecondary SortDirection PageSize int Extra Extra } type cursorTokenKeySortSerialize struct { ValuePrimary *string `json:"v1,omitempty"` ValueSecondary *string `json:"v2,omitempty"` Direction *SortDirection `json:"dir,omitempty"` DirectionSecondary *SortDirection `json:"dir2,omitempty"` PageSize *int `json:"size,omitempty"` ExtraTimestamp *time.Time `json:"ts,omitempty"` ExtraId *string `json:"id,omitempty"` ExtraPage *int `json:"pg,omitempty"` ExtraPageSize *int `json:"sz,omitempty"` } func NewKeySortToken(valuePrimary string, valueSecondary string, direction SortDirection, directionSecondary SortDirection, pageSize int, extra Extra) CursorToken { return CTKeySort{ Mode: CTMNormal, ValuePrimary: valuePrimary, ValueSecondary: valueSecondary, Direction: direction, DirectionSecondary: directionSecondary, PageSize: pageSize, Extra: extra, } } func Start() CursorToken { return CTKeySort{ Mode: CTMStart, ValuePrimary: "", ValueSecondary: "", Direction: "", DirectionSecondary: "", PageSize: 0, Extra: Extra{}, } } func End() CursorToken { return CTKeySort{ Mode: CTMEnd, ValuePrimary: "", ValueSecondary: "", Direction: "", DirectionSecondary: "", PageSize: 0, Extra: Extra{}, } } func (c CTKeySort) Token() string { if c.Mode == CTMStart { return "@start" } if c.Mode == CTMEnd { return "@end" } // We kinda manually implement omitempty for the CursorToken here // because omitempty does not work for time.Time and otherwise we would always // get weird time values when decoding a token that initially didn't have an Timestamp set // For this usecase we treat Unix=0 as an empty timestamp sertok := cursorTokenKeySortSerialize{} if c.ValuePrimary != "" { sertok.ValuePrimary = &c.ValuePrimary } if c.ValueSecondary != "" { sertok.ValueSecondary = &c.ValueSecondary } if c.Direction != "" { sertok.Direction = &c.Direction } if c.DirectionSecondary != "" { sertok.DirectionSecondary = &c.DirectionSecondary } if c.PageSize != 0 { sertok.PageSize = &c.PageSize } sertok.ExtraTimestamp = c.Extra.Timestamp sertok.ExtraId = c.Extra.Id sertok.ExtraPage = c.Extra.Page sertok.ExtraPageSize = c.Extra.PageSize body, err := json.Marshal(sertok) if err != nil { panic(err) } return "tok_" + base32.StdEncoding.EncodeToString(body) } func (c CTKeySort) IsEnd() bool { return c.Mode == CTMEnd } func (c CTKeySort) valuePrimaryObjectId() (primitive.ObjectID, bool) { if oid, err := primitive.ObjectIDFromHex(c.ValuePrimary); err == nil { return oid, true } else { return primitive.ObjectID{}, false } } func (c CTKeySort) valueSecondaryObjectId() (primitive.ObjectID, bool) { if oid, err := primitive.ObjectIDFromHex(c.ValueSecondary); err == nil { return oid, true } else { return primitive.ObjectID{}, false } }