v0.0.159
This commit is contained in:
parent
a30da61419
commit
ceff0161c6
@ -1,5 +1,5 @@
|
|||||||
package goext
|
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
|
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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -7,19 +7,56 @@ import (
|
|||||||
"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()
|
||||||
|
|
||||||
@ -50,7 +87,7 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int
|
|||||||
|
|
||||||
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 +99,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 +112,7 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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]
|
return c.dataTypeMap[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"
|
||||||
)
|
)
|
||||||
@ -177,3 +180,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)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user