Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
2569c165f8
|
|||
ee262a94fb
|
|||
7977c0e59c
|
|||
ceff0161c6
|
|||
a30da61419
|
@@ -1,5 +1,5 @@
|
|||||||
package goext
|
package goext
|
||||||
|
|
||||||
const GoextVersion = "0.0.157"
|
const GoextVersion = "0.0.162"
|
||||||
|
|
||||||
const GoextVersionTimestamp = "2023-06-10T16:22:14+0200"
|
const GoextVersionTimestamp = "2023-06-11T16:38:47+0200"
|
||||||
|
@@ -16,6 +16,9 @@ func CreateGoExtBsonRegistry() *bsoncodec.Registry {
|
|||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
||||||
|
|
||||||
|
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.Date{}), rfctime.Date{})
|
||||||
|
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.Date{}), rfctime.Date{})
|
||||||
|
|
||||||
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
|
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
|
||||||
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
|
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
|
||||||
|
|
||||||
|
240
rfctime/date.go
Normal file
240
rfctime/date.go
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
package rfctime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/bsonrw"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/bsontype"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Date struct {
|
||||||
|
Year int
|
||||||
|
Month int
|
||||||
|
Day int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) Time(loc *time.Location) time.Time {
|
||||||
|
return time.Date(t.Year, time.Month(t.Month), t.Day, 0, 0, 0, 0, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) TimeUTC() time.Time {
|
||||||
|
return time.Date(t.Year, time.Month(t.Month), t.Day, 0, 0, 0, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) TimeLocal() time.Time {
|
||||||
|
return time.Date(t.Year, time.Month(t.Month), t.Day, 0, 0, 0, 0, time.Local)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) MarshalBinary() ([]byte, error) {
|
||||||
|
return t.TimeUTC().MarshalBinary()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Date) UnmarshalBinary(data []byte) error {
|
||||||
|
nt := time.Time{}
|
||||||
|
if err := nt.UnmarshalBinary(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Year = nt.Year()
|
||||||
|
t.Month = int(nt.Month())
|
||||||
|
t.Day = nt.Day()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) GobEncode() ([]byte, error) {
|
||||||
|
return t.TimeUTC().GobEncode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Date) GobDecode(data []byte) error {
|
||||||
|
nt := time.Time{}
|
||||||
|
if err := nt.GobDecode(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Year = nt.Year()
|
||||||
|
t.Month = int(nt.Month())
|
||||||
|
t.Day = nt.Day()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Date) UnmarshalJSON(data []byte) error {
|
||||||
|
str := ""
|
||||||
|
if err := json.Unmarshal(data, &str); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t0, err := time.Parse(t.FormatStr(), str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Year = t0.Year()
|
||||||
|
t.Month = int(t0.Month())
|
||||||
|
t.Day = t0.Day()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) MarshalJSON() ([]byte, error) {
|
||||||
|
str := t.TimeUTC().Format(t.FormatStr())
|
||||||
|
return json.Marshal(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) MarshalText() ([]byte, error) {
|
||||||
|
b := make([]byte, 0, len(t.FormatStr()))
|
||||||
|
return t.TimeUTC().AppendFormat(b, t.FormatStr()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Date) UnmarshalText(data []byte) error {
|
||||||
|
var err error
|
||||||
|
v, err := time.Parse(t.FormatStr(), string(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Year = v.Year()
|
||||||
|
t.Month = int(v.Month())
|
||||||
|
t.Day = v.Day()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Date) UnmarshalBSONValue(bt bsontype.Type, data []byte) error {
|
||||||
|
if bt == bsontype.Null {
|
||||||
|
// we can't set nil in UnmarshalBSONValue (so we use default(struct))
|
||||||
|
// Use mongoext.CreateGoExtBsonRegistry if you need to unmarsh pointer values
|
||||||
|
// https://stackoverflow.com/questions/75167597
|
||||||
|
// https://jira.mongodb.org/browse/GODRIVER-2252
|
||||||
|
*t = Date{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if bt != bsontype.String {
|
||||||
|
return errors.New(fmt.Sprintf("cannot unmarshal %v into Date", bt))
|
||||||
|
}
|
||||||
|
|
||||||
|
var tt string
|
||||||
|
err := bson.RawValue{Type: bt, Value: data}.Unmarshal(&tt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := time.Parse(t.FormatStr(), tt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Year = v.Year()
|
||||||
|
t.Month = int(v.Month())
|
||||||
|
t.Day = v.Day()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) MarshalBSONValue() (bsontype.Type, []byte, error) {
|
||||||
|
return bson.MarshalValue(t.TimeUTC().Format(t.FormatStr()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) DecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
|
||||||
|
if val.Kind() == reflect.Ptr && val.IsNil() {
|
||||||
|
if !val.CanSet() {
|
||||||
|
return errors.New("ValueUnmarshalerDecodeValue")
|
||||||
|
}
|
||||||
|
val.Set(reflect.New(val.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
tp, src, err := bsonrw.Copier{}.CopyValueToBytes(vr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr && len(src) == 0 {
|
||||||
|
val.Set(reflect.Zero(val.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.UnmarshalBSONValue(tp, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val.Set(reflect.ValueOf(&t))
|
||||||
|
} else {
|
||||||
|
val.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) Serialize() string {
|
||||||
|
return t.TimeUTC().Format(t.FormatStr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) FormatStr() string {
|
||||||
|
return "2006-01-02"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) Date() (year int, month time.Month, day int) {
|
||||||
|
return t.TimeUTC().Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) Weekday() time.Weekday {
|
||||||
|
return t.TimeUTC().Weekday()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) ISOWeek() (year, week int) {
|
||||||
|
return t.TimeUTC().ISOWeek()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) YearDay() int {
|
||||||
|
return t.TimeUTC().YearDay()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) AddDate(years int, months int, days int) Date {
|
||||||
|
return NewDate(t.TimeUTC().AddDate(years, months, days))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) Unix() int64 {
|
||||||
|
return t.TimeUTC().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) UnixMilli() int64 {
|
||||||
|
return t.TimeUTC().UnixMilli()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) UnixMicro() int64 {
|
||||||
|
return t.TimeUTC().UnixMicro()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) UnixNano() int64 {
|
||||||
|
return t.TimeUTC().UnixNano()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) Format(layout string) string {
|
||||||
|
return t.TimeUTC().Format(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) GoString() string {
|
||||||
|
return t.TimeUTC().GoString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Date) String() string {
|
||||||
|
return t.TimeUTC().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDate(t time.Time) Date {
|
||||||
|
return Date{
|
||||||
|
Year: t.Year(),
|
||||||
|
Month: int(t.Month()),
|
||||||
|
Day: t.Day(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NowDate(loc *time.Location) Date {
|
||||||
|
return NewDate(time.Now().In(loc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NowDateLoc() Date {
|
||||||
|
return NewDate(time.Now().In(time.UTC))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NowDateUTC() Date {
|
||||||
|
return NewDate(time.Now().In(time.Local))
|
||||||
|
}
|
@@ -27,7 +27,7 @@ type Cursorable interface {
|
|||||||
Next(ctx context.Context) bool
|
Next(ctx context.Context) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type fullTypeRef[TData any] struct {
|
type fullTypeRef struct {
|
||||||
IsPointer bool
|
IsPointer bool
|
||||||
Kind reflect.Kind
|
Kind reflect.Kind
|
||||||
RealType reflect.Type
|
RealType reflect.Type
|
||||||
@@ -38,9 +38,11 @@ type fullTypeRef[TData any] struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Coll[TData any] struct {
|
type Coll[TData any] struct {
|
||||||
coll *mongo.Collection
|
coll *mongo.Collection // internal mongo collection, access via Collection()
|
||||||
dataTypeMap map[string]fullTypeRef[TData]
|
dataTypeMap map[string]fullTypeRef // list of TData fields (only if TData is not an interface)
|
||||||
customDecoder *func(ctx context.Context, dec Decodable) (TData, error)
|
implDataTypeMap map[reflect.Type]map[string]fullTypeRef // dynamic list of fields of TData implementations (only if TData is an interface)
|
||||||
|
customDecoder *func(ctx context.Context, dec Decodable) (TData, error) // custom decoding function (useful if TData is an interface)
|
||||||
|
isInterfaceDataType bool // true if TData is an interface (not a struct)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coll[TData]) Collection() *mongo.Collection {
|
func (c *Coll[TData]) Collection() *mongo.Collection {
|
||||||
@@ -51,7 +53,10 @@ func (c *Coll[TData]) Name() string {
|
|||||||
return c.coll.Name()
|
return c.coll.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coll[TData]) WithDecodeFunc(cdf func(ctx context.Context, dec Decodable) (TData, error)) *Coll[TData] {
|
func (c *Coll[TData]) WithDecodeFunc(cdf func(ctx context.Context, dec Decodable) (TData, error), example TData) *Coll[TData] {
|
||||||
|
|
||||||
|
c.EnsureInitializedReflection(example)
|
||||||
|
|
||||||
c.customDecoder = langext.Ptr(cdf)
|
c.customDecoder = langext.Ptr(cdf)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@@ -19,3 +19,20 @@ func (c *Coll[TData]) Aggregate(ctx context.Context, pipeline mongo.Pipeline, op
|
|||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Coll[TData]) AggregateOneOpt(ctx context.Context, pipeline mongo.Pipeline, opts ...*options.AggregateOptions) (*TData, error) {
|
||||||
|
cursor, err := c.coll.Aggregate(ctx, pipeline, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor.Next(ctx) {
|
||||||
|
v, err := c.decodeSingle(ctx, cursor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
@@ -62,6 +62,8 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
|||||||
|
|
||||||
last := entities[len(entities)-1]
|
last := entities[len(entities)-1]
|
||||||
|
|
||||||
|
c.EnsureInitializedReflection(last)
|
||||||
|
|
||||||
nextToken, err := c.createToken(sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, last, pageSize)
|
nextToken, err := c.createToken(sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, last, pageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ct.CursorToken{}, err
|
return nil, ct.CursorToken{}, err
|
||||||
|
@@ -1,25 +1,62 @@
|
|||||||
package wmo
|
package wmo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/reflectext"
|
"gogs.mikescher.com/BlackForestBytes/goext/reflectext"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Coll[TData]) init() {
|
func (c *Coll[TData]) EnsureInitializedReflection(v TData) {
|
||||||
|
|
||||||
c.dataTypeMap = make(map[string]fullTypeRef[TData])
|
if !c.isInterfaceDataType {
|
||||||
|
return // only dynamically load dataTypeMap on interface TData
|
||||||
|
}
|
||||||
|
|
||||||
|
rval := reflect.ValueOf(v)
|
||||||
|
for rval.Type().Kind() == reflect.Pointer {
|
||||||
|
rval = rval.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := c.implDataTypeMap[rval.Type()]; ok {
|
||||||
|
return // already loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]fullTypeRef)
|
||||||
|
|
||||||
|
c.initFields("", rval, m, make([]int, 0))
|
||||||
|
|
||||||
|
c.implDataTypeMap[rval.Type()] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coll[TData]) init() {
|
||||||
|
|
||||||
example := *new(TData)
|
example := *new(TData)
|
||||||
|
|
||||||
v := reflect.ValueOf(example)
|
datatype := reflect.TypeOf(&example).Elem()
|
||||||
|
|
||||||
c.initFields("", v, make([]int, 0))
|
if datatype.Kind() == reflect.Interface {
|
||||||
|
|
||||||
|
c.isInterfaceDataType = true
|
||||||
|
|
||||||
|
c.dataTypeMap = make(map[string]fullTypeRef)
|
||||||
|
c.implDataTypeMap = make(map[reflect.Type]map[string]fullTypeRef)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
c.isInterfaceDataType = false
|
||||||
|
|
||||||
|
c.dataTypeMap = make(map[string]fullTypeRef)
|
||||||
|
c.implDataTypeMap = make(map[reflect.Type]map[string]fullTypeRef)
|
||||||
|
|
||||||
|
v := reflect.ValueOf(example)
|
||||||
|
c.initFields("", v, c.dataTypeMap, make([]int, 0))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int) {
|
func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, m map[string]fullTypeRef, idxarr []int) {
|
||||||
|
|
||||||
rtyp := rval.Type()
|
rtyp := rval.Type()
|
||||||
|
|
||||||
@@ -32,25 +69,38 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bsontags := make([]string, 0)
|
||||||
bsonkey, found := rsfield.Tag.Lookup("bson")
|
bsonkey, found := rsfield.Tag.Lookup("bson")
|
||||||
if !found {
|
if !found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.Contains(bsonkey, ",") {
|
if strings.Contains(bsonkey, ",") {
|
||||||
|
bsontags = strings.Split(bsonkey[strings.Index(bsonkey, ",")+1:], ",")
|
||||||
bsonkey = bsonkey[:strings.Index(bsonkey, ",")]
|
bsonkey = bsonkey[:strings.Index(bsonkey, ",")]
|
||||||
}
|
}
|
||||||
if bsonkey == "-" {
|
if bsonkey == "-" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bsonkey == "" {
|
||||||
|
bsonkey = rsfield.Name
|
||||||
|
}
|
||||||
|
|
||||||
fullKey := prefix + bsonkey
|
fullKey := prefix + bsonkey
|
||||||
|
|
||||||
newIdxArr := langext.ArrCopy(idxarr)
|
newIdxArr := langext.ArrCopy(idxarr)
|
||||||
newIdxArr = append(newIdxArr, i)
|
newIdxArr = append(newIdxArr, i)
|
||||||
|
|
||||||
|
if langext.InArray("inline", bsontags) && rvfield.Kind() == reflect.Struct {
|
||||||
|
|
||||||
|
// pass-through field
|
||||||
|
c.initFields(prefix, rvfield, m, newIdxArr)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
if rvfield.Type().Kind() == reflect.Pointer {
|
if rvfield.Type().Kind() == reflect.Pointer {
|
||||||
|
|
||||||
c.dataTypeMap[fullKey] = fullTypeRef[TData]{
|
m[fullKey] = fullTypeRef{
|
||||||
IsPointer: true,
|
IsPointer: true,
|
||||||
RealType: rvfield.Type(),
|
RealType: rvfield.Type(),
|
||||||
Kind: rvfield.Type().Elem().Kind(),
|
Kind: rvfield.Type().Elem().Kind(),
|
||||||
@@ -62,7 +112,7 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
c.dataTypeMap[fullKey] = fullTypeRef[TData]{
|
m[fullKey] = fullTypeRef{
|
||||||
IsPointer: false,
|
IsPointer: false,
|
||||||
RealType: rvfield.Type(),
|
RealType: rvfield.Type(),
|
||||||
Kind: rvfield.Type().Kind(),
|
Kind: rvfield.Type().Kind(),
|
||||||
@@ -75,7 +125,9 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rvfield.Kind() == reflect.Struct {
|
if rvfield.Kind() == reflect.Struct {
|
||||||
c.initFields(fullKey+".", rvfield, newIdxArr)
|
c.initFields(fullKey+".", rvfield, m, newIdxArr)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -84,7 +136,10 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
|||||||
|
|
||||||
func (c *Coll[TData]) getTokenValueAsMongoType(value string, fieldName string) (any, error) {
|
func (c *Coll[TData]) getTokenValueAsMongoType(value string, fieldName string) (any, error) {
|
||||||
|
|
||||||
fref := c.dataTypeMap[fieldName]
|
fref, err := c.getFieldType(fieldName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
pss := reflectext.PrimitiveStringSerializer{}
|
pss := reflectext.PrimitiveStringSerializer{}
|
||||||
|
|
||||||
@@ -94,7 +149,10 @@ func (c *Coll[TData]) getTokenValueAsMongoType(value string, fieldName string) (
|
|||||||
|
|
||||||
func (c *Coll[TData]) getFieldValueAsTokenString(entity TData, fieldName string) (string, error) {
|
func (c *Coll[TData]) getFieldValueAsTokenString(entity TData, fieldName string) (string, error) {
|
||||||
|
|
||||||
realValue := c.getFieldValue(entity, fieldName)
|
realValue, err := c.getFieldValue(entity, fieldName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
pss := reflectext.PrimitiveStringSerializer{}
|
pss := reflectext.PrimitiveStringSerializer{}
|
||||||
|
|
||||||
@@ -102,12 +160,56 @@ func (c *Coll[TData]) getFieldValueAsTokenString(entity TData, fieldName string)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coll[TData]) getFieldType(fieldName string) fullTypeRef[TData] {
|
func (c *Coll[TData]) getFieldType(fieldName string) (fullTypeRef, error) {
|
||||||
return c.dataTypeMap[fieldName]
|
if c.isInterfaceDataType {
|
||||||
|
|
||||||
|
for _, m := range c.implDataTypeMap {
|
||||||
|
if r, ok := m[fieldName]; ok {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullTypeRef{}, errors.New("unknown field: '" + fieldName + "' (in any impl)")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if r, ok := c.dataTypeMap[fieldName]; ok {
|
||||||
|
return r, nil
|
||||||
|
} else {
|
||||||
|
return fullTypeRef{}, errors.New("unknown field: '" + fieldName + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coll[TData]) getFieldValue(data TData, fieldName string) any {
|
func (c *Coll[TData]) getFieldValue(data TData, fieldName string) (any, error) {
|
||||||
fref := c.dataTypeMap[fieldName]
|
if c.isInterfaceDataType {
|
||||||
|
|
||||||
rval := reflect.ValueOf(data)
|
rval := reflect.ValueOf(data)
|
||||||
return rval.FieldByIndex(fref.Index).Interface()
|
for rval.Type().Kind() == reflect.Pointer {
|
||||||
|
rval = rval.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := c.implDataTypeMap[rval.Type()]; ok {
|
||||||
|
if fref, ok := m[fieldName]; ok {
|
||||||
|
rval := reflect.ValueOf(data)
|
||||||
|
return rval.FieldByIndex(fref.Index).Interface(), nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unknown bson field '" + fieldName + "' in type '" + rval.Type().String() + "'")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unknown TData type: '" + rval.Type().String() + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if fref, ok := c.dataTypeMap[fieldName]; ok {
|
||||||
|
rval := reflect.ValueOf(data)
|
||||||
|
return rval.FieldByIndex(fref.Index).Interface(), nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unknown bson field '" + fieldName + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
package wmo
|
package wmo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/rfctime"
|
"gogs.mikescher.com/BlackForestBytes/goext/rfctime"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/timeext"
|
"gogs.mikescher.com/BlackForestBytes/goext/timeext"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/tst"
|
"gogs.mikescher.com/BlackForestBytes/goext/tst"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -45,35 +48,51 @@ func TestReflectionGetFieldType(t *testing.T) {
|
|||||||
MDate: t1,
|
MDate: t1,
|
||||||
}
|
}
|
||||||
|
|
||||||
tst.AssertEqual(t, coll.getFieldType("_id").Kind.String(), "string")
|
gft := func(k string) fullTypeRef {
|
||||||
tst.AssertEqual(t, coll.getFieldType("_id").Type.String(), "wmo.IDType")
|
v, err := coll.getFieldType(k)
|
||||||
tst.AssertEqual(t, coll.getFieldType("_id").Name, "ID")
|
if err != nil {
|
||||||
tst.AssertEqual(t, coll.getFieldType("_id").IsPointer, false)
|
t.Errorf("%s: %v", "failed to getFieldType", err)
|
||||||
tst.AssertEqual(t, coll.getFieldValue(d, "_id").(IDType), "1")
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
tst.AssertEqual(t, coll.getFieldType("cdate").Kind.String(), "struct")
|
gfv := func(k string) any {
|
||||||
tst.AssertEqual(t, coll.getFieldType("cdate").Type.String(), "time.Time")
|
v, err := coll.getFieldValue(d, k)
|
||||||
tst.AssertEqual(t, coll.getFieldType("cdate").Name, "CDate")
|
if err != nil {
|
||||||
tst.AssertEqual(t, coll.getFieldType("cdate").IsPointer, false)
|
t.Errorf("%s: %v", "failed to getFieldType", err)
|
||||||
tst.AssertEqual(t, coll.getFieldValue(d, "cdate").(time.Time), t0)
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
tst.AssertEqual(t, coll.getFieldType("sub.a").Kind.String(), "string")
|
tst.AssertEqual(t, gft("_id").Kind.String(), "string")
|
||||||
tst.AssertEqual(t, coll.getFieldType("sub.a").Type.String(), "string")
|
tst.AssertEqual(t, gft("_id").Type.String(), "wmo.IDType")
|
||||||
tst.AssertEqual(t, coll.getFieldType("sub.a").Name, "A")
|
tst.AssertEqual(t, gft("_id").Name, "ID")
|
||||||
tst.AssertEqual(t, coll.getFieldType("sub.a").IsPointer, false)
|
tst.AssertEqual(t, gft("_id").IsPointer, false)
|
||||||
tst.AssertEqual(t, coll.getFieldValue(d, "sub.a").(string), "2")
|
tst.AssertEqual(t, gfv("_id").(IDType), "1")
|
||||||
|
|
||||||
tst.AssertEqual(t, coll.getFieldType("str").Kind.String(), "string")
|
tst.AssertEqual(t, gft("cdate").Kind.String(), "struct")
|
||||||
tst.AssertEqual(t, coll.getFieldType("str").Type.String(), "string")
|
tst.AssertEqual(t, gft("cdate").Type.String(), "time.Time")
|
||||||
tst.AssertEqual(t, coll.getFieldType("str").Name, "Str")
|
tst.AssertEqual(t, gft("cdate").Name, "CDate")
|
||||||
tst.AssertEqual(t, coll.getFieldType("str").IsPointer, false)
|
tst.AssertEqual(t, gft("cdate").IsPointer, false)
|
||||||
tst.AssertEqual(t, coll.getFieldValue(d, "str").(string), "3")
|
tst.AssertEqual(t, gfv("cdate").(time.Time), t0)
|
||||||
|
|
||||||
tst.AssertEqual(t, coll.getFieldType("ptr").Kind.String(), "int")
|
tst.AssertEqual(t, gft("sub.a").Kind.String(), "string")
|
||||||
tst.AssertEqual(t, coll.getFieldType("ptr").Type.String(), "int")
|
tst.AssertEqual(t, gft("sub.a").Type.String(), "string")
|
||||||
tst.AssertEqual(t, coll.getFieldType("ptr").Name, "Ptr")
|
tst.AssertEqual(t, gft("sub.a").Name, "A")
|
||||||
tst.AssertEqual(t, coll.getFieldType("ptr").IsPointer, true)
|
tst.AssertEqual(t, gft("sub.a").IsPointer, false)
|
||||||
tst.AssertEqual(t, *coll.getFieldValue(d, "ptr").(*int), 4)
|
tst.AssertEqual(t, gfv("sub.a").(string), "2")
|
||||||
|
|
||||||
|
tst.AssertEqual(t, gft("str").Kind.String(), "string")
|
||||||
|
tst.AssertEqual(t, gft("str").Type.String(), "string")
|
||||||
|
tst.AssertEqual(t, gft("str").Name, "Str")
|
||||||
|
tst.AssertEqual(t, gft("str").IsPointer, false)
|
||||||
|
tst.AssertEqual(t, gfv("str").(string), "3")
|
||||||
|
|
||||||
|
tst.AssertEqual(t, gft("ptr").Kind.String(), "int")
|
||||||
|
tst.AssertEqual(t, gft("ptr").Type.String(), "int")
|
||||||
|
tst.AssertEqual(t, gft("ptr").Name, "Ptr")
|
||||||
|
tst.AssertEqual(t, gft("ptr").IsPointer, true)
|
||||||
|
tst.AssertEqual(t, *gfv("ptr").(*int), 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReflectionGetTokenValueAsMongoType(t *testing.T) {
|
func TestReflectionGetTokenValueAsMongoType(t *testing.T) {
|
||||||
@@ -177,3 +196,31 @@ func TestReflectionGetFieldValueAsTokenString(t *testing.T) {
|
|||||||
tst.AssertEqual(t, gfvats(d, "cdate"), t0.Format(time.RFC3339Nano))
|
tst.AssertEqual(t, gfvats(d, "cdate"), t0.Format(time.RFC3339Nano))
|
||||||
tst.AssertEqual(t, gfvats(d, "mdate"), t0.Format(time.RFC3339Nano))
|
tst.AssertEqual(t, gfvats(d, "mdate"), t0.Format(time.RFC3339Nano))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReflectionWithInterface(t *testing.T) {
|
||||||
|
|
||||||
|
type TestData struct {
|
||||||
|
ID primitive.ObjectID `bson:"_id"`
|
||||||
|
CDate time.Time `bson:"cdate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestInterface interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
coll1 := W[TestInterface](&mongo.Collection{})
|
||||||
|
|
||||||
|
tst.AssertTrue(t, coll1.coll != nil)
|
||||||
|
tst.AssertEqual(t, 0, len(coll1.implDataTypeMap))
|
||||||
|
|
||||||
|
df := func(ctx context.Context, dec Decodable) (TestInterface, error) {
|
||||||
|
return TestData{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
coll2 := W[TestInterface](&mongo.Collection{}).WithDecodeFunc(df, TestData{})
|
||||||
|
|
||||||
|
tst.AssertTrue(t, coll2.coll != nil)
|
||||||
|
tst.AssertEqual(t, 1, len(coll2.implDataTypeMap))
|
||||||
|
|
||||||
|
tst.AssertEqual(t, "ID", coll2.implDataTypeMap[reflect.TypeOf(TestData{})]["_id"].Name)
|
||||||
|
tst.AssertEqual(t, "CDate", coll2.implDataTypeMap[reflect.TypeOf(TestData{})]["cdate"].Name)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user