v0.0.159
This commit is contained in:
parent
a30da61419
commit
ceff0161c6
@ -1,5 +1,5 @@
|
||||
package goext
|
||||
|
||||
const GoextVersion = "0.0.158"
|
||||
const GoextVersion = "0.0.159"
|
||||
|
||||
const GoextVersionTimestamp = "2023-06-10T16:28:50+0200"
|
||||
const GoextVersionTimestamp = "2023-06-10T18:35:56+0200"
|
||||
|
@ -27,7 +27,7 @@ type Cursorable interface {
|
||||
Next(ctx context.Context) bool
|
||||
}
|
||||
|
||||
type fullTypeRef[TData any] struct {
|
||||
type fullTypeRef struct {
|
||||
IsPointer bool
|
||||
Kind reflect.Kind
|
||||
RealType reflect.Type
|
||||
@ -38,9 +38,11 @@ type fullTypeRef[TData any] struct {
|
||||
}
|
||||
|
||||
type Coll[TData any] struct {
|
||||
coll *mongo.Collection
|
||||
dataTypeMap map[string]fullTypeRef[TData]
|
||||
customDecoder *func(ctx context.Context, dec Decodable) (TData, error)
|
||||
coll *mongo.Collection // internal mongo collection, access via Collection()
|
||||
dataTypeMap map[string]fullTypeRef // list of TData fields (only if TData is not an interface)
|
||||
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 {
|
||||
@ -51,7 +53,10 @@ func (c *Coll[TData]) Name() string {
|
||||
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)
|
||||
return c
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
|
||||
last := entities[len(entities)-1]
|
||||
|
||||
c.EnsureInitializedReflection(last)
|
||||
|
||||
nextToken, err := c.createToken(sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, last, pageSize)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, err
|
||||
|
@ -7,19 +7,56 @@ import (
|
||||
"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)
|
||||
|
||||
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()
|
||||
|
||||
@ -50,7 +87,7 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
||||
|
||||
if rvfield.Type().Kind() == reflect.Pointer {
|
||||
|
||||
c.dataTypeMap[fullKey] = fullTypeRef[TData]{
|
||||
m[fullKey] = fullTypeRef{
|
||||
IsPointer: true,
|
||||
RealType: rvfield.Type(),
|
||||
Kind: rvfield.Type().Elem().Kind(),
|
||||
@ -62,7 +99,7 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
||||
|
||||
} else {
|
||||
|
||||
c.dataTypeMap[fullKey] = fullTypeRef[TData]{
|
||||
m[fullKey] = fullTypeRef{
|
||||
IsPointer: false,
|
||||
RealType: rvfield.Type(),
|
||||
Kind: rvfield.Type().Kind(),
|
||||
@ -75,7 +112,7 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
||||
}
|
||||
|
||||
if rvfield.Kind() == reflect.Struct {
|
||||
c.initFields(fullKey+".", rvfield, newIdxArr)
|
||||
c.initFields(fullKey+".", rvfield, m, newIdxArr)
|
||||
}
|
||||
|
||||
}
|
||||
@ -102,7 +139,7 @@ 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 {
|
||||
return c.dataTypeMap[fieldName]
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
package wmo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/rfctime"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/timeext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/tst"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -177,3 +180,31 @@ func TestReflectionGetFieldValueAsTokenString(t *testing.T) {
|
||||
tst.AssertEqual(t, gfvats(d, "cdate"), 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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user