2023-06-06 21:18:40 +02:00
package wmo
import (
2023-08-21 15:08:35 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
2023-06-06 21:18:40 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/langext"
2023-06-07 16:58:17 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/reflectext"
2023-06-06 21:18:40 +02:00
"reflect"
2023-06-07 17:48:36 +02:00
"strings"
2023-06-06 21:18:40 +02:00
)
2023-06-10 18:35:56 +02:00
func ( c * Coll [ TData ] ) EnsureInitializedReflection ( v 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 )
2023-09-12 10:47:41 +02:00
c . initFields ( "" , rval . Type ( ) , m , make ( [ ] int , 0 ) )
2023-06-06 21:18:40 +02:00
2023-06-10 18:35:56 +02:00
c . implDataTypeMap [ rval . Type ( ) ] = m
}
func ( c * Coll [ TData ] ) init ( ) {
2023-06-06 21:18:40 +02:00
example := * new ( TData )
2023-06-10 18:35:56 +02:00
datatype := reflect . TypeOf ( & example ) . Elem ( )
if datatype . Kind ( ) == reflect . Interface {
c . isInterfaceDataType = true
2023-06-06 21:18:40 +02:00
2023-06-10 18:35:56 +02:00
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 )
2023-09-12 10:47:41 +02:00
c . initFields ( "" , v . Type ( ) , c . dataTypeMap , make ( [ ] int , 0 ) )
2023-06-10 18:35:56 +02:00
}
2023-06-06 21:18:40 +02:00
}
2023-09-12 10:47:41 +02:00
func ( c * Coll [ TData ] ) initFields ( prefix string , rtyp reflect . Type , m map [ string ] fullTypeRef , idxarr [ ] int ) {
2023-06-06 21:18:40 +02:00
for i := 0 ; i < rtyp . NumField ( ) ; i ++ {
rsfield := rtyp . Field ( i )
if ! rsfield . IsExported ( ) {
continue
}
2023-06-11 16:35:20 +02:00
bsontags := make ( [ ] string , 0 )
2023-06-06 21:18:40 +02:00
bsonkey , found := rsfield . Tag . Lookup ( "bson" )
2023-06-07 17:48:36 +02:00
if ! found {
continue
}
if strings . Contains ( bsonkey , "," ) {
2023-06-11 16:35:20 +02:00
bsontags = strings . Split ( bsonkey [ strings . Index ( bsonkey , "," ) + 1 : ] , "," )
2023-06-11 16:38:47 +02:00
bsonkey = bsonkey [ : strings . Index ( bsonkey , "," ) ]
2023-06-07 17:48:36 +02:00
}
if bsonkey == "-" {
2023-06-06 21:18:40 +02:00
continue
}
2023-06-11 16:35:20 +02:00
if bsonkey == "" {
bsonkey = rsfield . Name
}
2023-06-06 21:18:40 +02:00
fullKey := prefix + bsonkey
newIdxArr := langext . ArrCopy ( idxarr )
newIdxArr = append ( newIdxArr , i )
2023-09-12 10:47:41 +02:00
if langext . InArray ( "inline" , bsontags ) && rsfield . Type . Kind ( ) == reflect . Struct {
2023-06-06 21:18:40 +02:00
2023-06-11 16:35:20 +02:00
// pass-through field
2023-09-12 10:47:41 +02:00
c . initFields ( prefix , rsfield . Type , m , newIdxArr )
2023-06-06 21:18:40 +02:00
} else {
2023-09-12 10:47:41 +02:00
if rsfield . Type . Kind ( ) == reflect . Pointer {
2023-06-11 16:35:20 +02:00
m [ fullKey ] = fullTypeRef {
IsPointer : true ,
2023-09-12 10:47:41 +02:00
RealType : rsfield . Type ,
Kind : rsfield . Type . Elem ( ) . Kind ( ) ,
Type : rsfield . Type . Elem ( ) ,
UnderlyingType : reflectext . Underlying ( rsfield . Type . Elem ( ) ) ,
2023-06-11 16:35:20 +02:00
Name : rsfield . Name ,
Index : newIdxArr ,
}
} else {
m [ fullKey ] = fullTypeRef {
IsPointer : false ,
2023-09-12 10:47:41 +02:00
RealType : rsfield . Type ,
Kind : rsfield . Type . Kind ( ) ,
Type : rsfield . Type ,
UnderlyingType : reflectext . Underlying ( rsfield . Type ) ,
2023-06-11 16:35:20 +02:00
Name : rsfield . Name ,
Index : newIdxArr ,
}
2023-06-06 21:18:40 +02:00
}
2023-09-12 10:47:41 +02:00
if rsfield . Type . Kind ( ) == reflect . Struct {
c . initFields ( fullKey + "." , rsfield . Type , m , newIdxArr )
2023-06-11 16:35:20 +02:00
}
2023-06-06 21:18:40 +02:00
2023-09-12 10:47:41 +02:00
if rsfield . Type . Kind ( ) == reflect . Pointer && rsfield . Type . Elem ( ) . Kind ( ) == reflect . Struct {
c . initFields ( fullKey + "." , rsfield . Type . Elem ( ) , m , newIdxArr )
}
2023-06-06 21:18:40 +02:00
}
}
}
func ( c * Coll [ TData ] ) getTokenValueAsMongoType ( value string , fieldName string ) ( any , error ) {
2023-06-11 16:35:20 +02:00
fref , err := c . getFieldType ( fieldName )
if err != nil {
2023-08-21 15:08:35 +02:00
return nil , exerr . Wrap ( err , "failed to get-field-type" ) . Str ( "fieldName" , fieldName ) . Build ( )
2023-06-11 16:35:20 +02:00
}
2023-06-06 21:18:40 +02:00
2023-06-07 16:58:17 +02:00
pss := reflectext . PrimitiveStringSerializer { }
2023-06-06 21:18:40 +02:00
2023-06-07 16:58:17 +02:00
return pss . ValueFromString ( value , fref . RealType )
2023-06-06 21:18:40 +02:00
}
func ( c * Coll [ TData ] ) getFieldValueAsTokenString ( entity TData , fieldName string ) ( string , error ) {
2023-06-11 16:35:20 +02:00
realValue , err := c . getFieldValue ( entity , fieldName )
if err != nil {
2023-08-21 15:08:35 +02:00
return "" , exerr . Wrap ( err , "failed to get-field-value" ) . Str ( "fieldName" , fieldName ) . Build ( )
2023-06-11 16:35:20 +02:00
}
2023-06-06 21:18:40 +02:00
2023-06-07 16:58:17 +02:00
pss := reflectext . PrimitiveStringSerializer { }
2023-06-06 21:18:40 +02:00
2023-06-07 16:58:17 +02:00
return pss . ValueToString ( realValue )
2023-06-06 21:18:40 +02:00
}
2023-06-11 16:35:20 +02:00
func ( c * Coll [ TData ] ) getFieldType ( fieldName string ) ( fullTypeRef , error ) {
if c . isInterfaceDataType {
for _ , m := range c . implDataTypeMap {
if r , ok := m [ fieldName ] ; ok {
return r , nil
}
}
2023-08-21 15:08:35 +02:00
return fullTypeRef { } , exerr . New ( exerr . TypeMongoReflection , "unknown field: '" + fieldName + "' (in any impl)" ) . Str ( "fieldName" , fieldName ) . Build ( )
2023-06-11 16:35:20 +02:00
} else {
if r , ok := c . dataTypeMap [ fieldName ] ; ok {
return r , nil
} else {
2023-08-21 15:08:35 +02:00
return fullTypeRef { } , exerr . New ( exerr . TypeMongoReflection , "unknown field: '" + fieldName + "'" ) . Str ( "fieldName" , fieldName ) . Build ( )
2023-06-11 16:35:20 +02:00
}
}
2023-06-06 21:18:40 +02:00
}
2023-06-11 16:35:20 +02:00
func ( c * Coll [ TData ] ) getFieldValue ( data TData , fieldName string ) ( any , error ) {
if c . isInterfaceDataType {
rval := reflect . ValueOf ( data )
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 {
2023-08-21 15:08:35 +02:00
return nil , exerr . New ( exerr . TypeMongoReflection , "unknown bson field '" + fieldName + "' in type '" + rval . Type ( ) . String ( ) + "'" ) . Str ( "fieldName" , fieldName ) . Type ( "rval" , rval ) . Build ( )
2023-06-11 16:35:20 +02:00
}
} else {
2023-08-21 15:08:35 +02:00
return nil , exerr . New ( exerr . TypeMongoReflection , "unknown TData type: '" + rval . Type ( ) . String ( ) + "'" ) . Type ( "rval" , rval ) . Build ( )
2023-06-11 16:35:20 +02:00
}
} else {
if fref , ok := c . dataTypeMap [ fieldName ] ; ok {
rval := reflect . ValueOf ( data )
return rval . FieldByIndex ( fref . Index ) . Interface ( ) , nil
} else {
2023-08-21 15:08:35 +02:00
return nil , exerr . New ( exerr . TypeMongoReflection , "unknown bson field '" + fieldName + "'" ) . Str ( "fieldName" , fieldName ) . Build ( )
2023-06-11 16:35:20 +02:00
}
}
2023-06-06 21:18:40 +02:00
}