Compare commits
	
		
			7 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d88cd3c22b | |||
| ac5ad640bd | |||
| 21d241f9b1 | |||
| 2569c165f8 | |||
| ee262a94fb | |||
| 7977c0e59c | |||
| ceff0161c6 | 
| @@ -178,9 +178,9 @@ func parseEnvToValue(envval string, fullEnvKey string, rvtype reflect.Type) (ref | ||||
| 		if strings.TrimSpace(strings.ToLower(envval)) == "true" { | ||||
| 			return reflect.ValueOf(true).Convert(rvtype), nil | ||||
| 		} else if strings.TrimSpace(strings.ToLower(envval)) == "false" { | ||||
| 			return reflect.ValueOf(true).Convert(rvtype), nil | ||||
| 		} else if strings.TrimSpace(strings.ToLower(envval)) == "1" { | ||||
| 			return reflect.ValueOf(false).Convert(rvtype), nil | ||||
| 		} else if strings.TrimSpace(strings.ToLower(envval)) == "1" { | ||||
| 			return reflect.ValueOf(true).Convert(rvtype), nil | ||||
| 		} else if strings.TrimSpace(strings.ToLower(envval)) == "0" { | ||||
| 			return reflect.ValueOf(false).Convert(rvtype), nil | ||||
| 		} else { | ||||
|   | ||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
								
							| @@ -4,21 +4,21 @@ go 1.19 | ||||
|  | ||||
| require ( | ||||
| 	github.com/jmoiron/sqlx v1.3.5 | ||||
| 	go.mongodb.org/mongo-driver v1.11.7 | ||||
| 	golang.org/x/crypto v0.9.0 | ||||
| 	golang.org/x/sys v0.8.0 | ||||
| 	golang.org/x/term v0.8.0 | ||||
| 	go.mongodb.org/mongo-driver v1.12.0 | ||||
| 	golang.org/x/crypto v0.10.0 | ||||
| 	golang.org/x/sys v0.9.0 | ||||
| 	golang.org/x/term v0.9.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/golang/snappy v0.0.4 // indirect | ||||
| 	github.com/klauspost/compress v1.16.5 // indirect | ||||
| 	github.com/klauspost/compress v1.16.6 // indirect | ||||
| 	github.com/montanaflynn/stats v0.7.1 // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect | ||||
| 	github.com/xdg-go/scram v1.1.2 // indirect | ||||
| 	github.com/xdg-go/stringprep v1.0.4 // indirect | ||||
| 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect | ||||
| 	golang.org/x/sync v0.2.0 // indirect | ||||
| 	golang.org/x/text v0.9.0 // indirect | ||||
| 	golang.org/x/sync v0.3.0 // indirect | ||||
| 	golang.org/x/text v0.10.0 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										15
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								go.sum
									
									
									
									
									
								
							| @@ -15,6 +15,8 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ | ||||
| github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= | ||||
| github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= | ||||
| github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||
| github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= | ||||
| github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||
| github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| @@ -56,6 +58,8 @@ go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7 | ||||
| go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= | ||||
| go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= | ||||
| go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= | ||||
| go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= | ||||
| go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| @@ -64,6 +68,8 @@ golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= | ||||
| golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= | ||||
| golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= | ||||
| golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= | ||||
| golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= | ||||
| golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= | ||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| @@ -76,6 +82,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= | ||||
| golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= | ||||
| golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @@ -87,12 +95,16 @@ golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= | ||||
| golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= | ||||
| golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= | ||||
| golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= | ||||
| golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= | ||||
| golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= | ||||
| golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= | ||||
| golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= | ||||
| golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| @@ -100,8 +112,11 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= | ||||
| golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= | ||||
| golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= | ||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= | ||||
| golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| package goext | ||||
|  | ||||
| const GoextVersion = "0.0.158" | ||||
| const GoextVersion = "0.0.167" | ||||
|  | ||||
| const GoextVersionTimestamp = "2023-06-10T16:28:50+0200" | ||||
| const GoextVersionTimestamp = "2023-06-22T17:33:56+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.Date{}), rfctime.Date{}) | ||||
| 	rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.Date{}), rfctime.Date{}) | ||||
|  | ||||
| 	bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(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 | ||||
| } | ||||
|  | ||||
| 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 | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package wmo | ||||
| import ( | ||||
| 	"context" | ||||
| 	"go.mongodb.org/mongo-driver/bson" | ||||
| 	"go.mongodb.org/mongo-driver/mongo" | ||||
| 	ct "gogs.mikescher.com/BlackForestBytes/goext/cursortoken" | ||||
| ) | ||||
|  | ||||
| @@ -11,9 +12,16 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int, | ||||
| 		return make([]TData, 0), ct.End(), nil | ||||
| 	} | ||||
|  | ||||
| 	pipeline := filter.FilterQuery() | ||||
| 	pipeline := mongo.Pipeline{} | ||||
| 	pf1 := "_id" | ||||
| 	pd1 := ct.SortASC | ||||
| 	pf2 := "_id" | ||||
| 	pd2 := ct.SortASC | ||||
|  | ||||
| 	pf1, pd1, pf2, pd2 := filter.Pagination() | ||||
| 	if filter != nil { | ||||
| 		pipeline = filter.FilterQuery() | ||||
| 		pf1, pd1, pf2, pd2 = filter.Pagination() | ||||
| 	} | ||||
|  | ||||
| 	sortPrimary := pf1 | ||||
| 	sortDirPrimary := pd1 | ||||
| @@ -62,6 +70,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 | ||||
|   | ||||
| @@ -1,25 +1,62 @@ | ||||
| package wmo | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/reflectext" | ||||
| 	"reflect" | ||||
| 	"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() | ||||
|  | ||||
| @@ -32,50 +69,65 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, idxarr []int | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		bsontags := make([]string, 0) | ||||
| 		bsonkey, found := rsfield.Tag.Lookup("bson") | ||||
| 		if !found { | ||||
| 			continue | ||||
| 		} | ||||
| 		if strings.Contains(bsonkey, ",") { | ||||
| 			bsontags = strings.Split(bsonkey[strings.Index(bsonkey, ",")+1:], ",") | ||||
| 			bsonkey = bsonkey[:strings.Index(bsonkey, ",")] | ||||
| 		} | ||||
| 		if bsonkey == "-" { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if bsonkey == "" { | ||||
| 			bsonkey = rsfield.Name | ||||
| 		} | ||||
|  | ||||
| 		fullKey := prefix + bsonkey | ||||
|  | ||||
| 		newIdxArr := langext.ArrCopy(idxarr) | ||||
| 		newIdxArr = append(newIdxArr, i) | ||||
|  | ||||
| 		if rvfield.Type().Kind() == reflect.Pointer { | ||||
| 		if langext.InArray("inline", bsontags) && rvfield.Kind() == reflect.Struct { | ||||
|  | ||||
| 			c.dataTypeMap[fullKey] = fullTypeRef[TData]{ | ||||
| 				IsPointer:      true, | ||||
| 				RealType:       rvfield.Type(), | ||||
| 				Kind:           rvfield.Type().Elem().Kind(), | ||||
| 				Type:           rvfield.Type().Elem(), | ||||
| 				UnderlyingType: reflectext.Underlying(rvfield.Type().Elem()), | ||||
| 				Name:           rsfield.Name, | ||||
| 				Index:          newIdxArr, | ||||
| 			} | ||||
| 			// pass-through field | ||||
| 			c.initFields(prefix, rvfield, m, newIdxArr) | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			c.dataTypeMap[fullKey] = fullTypeRef[TData]{ | ||||
| 				IsPointer:      false, | ||||
| 				RealType:       rvfield.Type(), | ||||
| 				Kind:           rvfield.Type().Kind(), | ||||
| 				Type:           rvfield.Type(), | ||||
| 				UnderlyingType: reflectext.Underlying(rvfield.Type()), | ||||
| 				Name:           rsfield.Name, | ||||
| 				Index:          newIdxArr, | ||||
| 			if rvfield.Type().Kind() == reflect.Pointer { | ||||
|  | ||||
| 				m[fullKey] = fullTypeRef{ | ||||
| 					IsPointer:      true, | ||||
| 					RealType:       rvfield.Type(), | ||||
| 					Kind:           rvfield.Type().Elem().Kind(), | ||||
| 					Type:           rvfield.Type().Elem(), | ||||
| 					UnderlyingType: reflectext.Underlying(rvfield.Type().Elem()), | ||||
| 					Name:           rsfield.Name, | ||||
| 					Index:          newIdxArr, | ||||
| 				} | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				m[fullKey] = fullTypeRef{ | ||||
| 					IsPointer:      false, | ||||
| 					RealType:       rvfield.Type(), | ||||
| 					Kind:           rvfield.Type().Kind(), | ||||
| 					Type:           rvfield.Type(), | ||||
| 					UnderlyingType: reflectext.Underlying(rvfield.Type()), | ||||
| 					Name:           rsfield.Name, | ||||
| 					Index:          newIdxArr, | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 			if rvfield.Kind() == reflect.Struct { | ||||
| 				c.initFields(fullKey+".", rvfield, m, newIdxArr) | ||||
| 			} | ||||
|  | ||||
| 		if rvfield.Kind() == reflect.Struct { | ||||
| 			c.initFields(fullKey+".", rvfield, 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) { | ||||
|  | ||||
| 	fref := c.dataTypeMap[fieldName] | ||||
| 	fref, err := c.getFieldType(fieldName) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
|  | ||||
| 	realValue := c.getFieldValue(entity, fieldName) | ||||
| 	realValue, err := c.getFieldValue(entity, fieldName) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	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] { | ||||
| 	return c.dataTypeMap[fieldName] | ||||
| 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 | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		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 { | ||||
| 	fref := c.dataTypeMap[fieldName] | ||||
| 	rval := reflect.ValueOf(data) | ||||
| 	return rval.FieldByIndex(fref.Index).Interface() | ||||
| 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 { | ||||
| 				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 | ||||
|  | ||||
| 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" | ||||
| ) | ||||
| @@ -45,35 +48,51 @@ func TestReflectionGetFieldType(t *testing.T) { | ||||
| 		MDate: t1, | ||||
| 	} | ||||
|  | ||||
| 	tst.AssertEqual(t, coll.getFieldType("_id").Kind.String(), "string") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("_id").Type.String(), "wmo.IDType") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("_id").Name, "ID") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("_id").IsPointer, false) | ||||
| 	tst.AssertEqual(t, coll.getFieldValue(d, "_id").(IDType), "1") | ||||
| 	gft := func(k string) fullTypeRef { | ||||
| 		v, err := coll.getFieldType(k) | ||||
| 		if err != nil { | ||||
| 			t.Errorf("%s: %v", "failed to getFieldType", err) | ||||
| 		} | ||||
| 		return v | ||||
| 	} | ||||
|  | ||||
| 	tst.AssertEqual(t, coll.getFieldType("cdate").Kind.String(), "struct") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("cdate").Type.String(), "time.Time") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("cdate").Name, "CDate") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("cdate").IsPointer, false) | ||||
| 	tst.AssertEqual(t, coll.getFieldValue(d, "cdate").(time.Time), t0) | ||||
| 	gfv := func(k string) any { | ||||
| 		v, err := coll.getFieldValue(d, k) | ||||
| 		if err != nil { | ||||
| 			t.Errorf("%s: %v", "failed to getFieldType", err) | ||||
| 		} | ||||
| 		return v | ||||
| 	} | ||||
|  | ||||
| 	tst.AssertEqual(t, coll.getFieldType("sub.a").Kind.String(), "string") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("sub.a").Type.String(), "string") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("sub.a").Name, "A") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("sub.a").IsPointer, false) | ||||
| 	tst.AssertEqual(t, coll.getFieldValue(d, "sub.a").(string), "2") | ||||
| 	tst.AssertEqual(t, gft("_id").Kind.String(), "string") | ||||
| 	tst.AssertEqual(t, gft("_id").Type.String(), "wmo.IDType") | ||||
| 	tst.AssertEqual(t, gft("_id").Name, "ID") | ||||
| 	tst.AssertEqual(t, gft("_id").IsPointer, false) | ||||
| 	tst.AssertEqual(t, gfv("_id").(IDType), "1") | ||||
|  | ||||
| 	tst.AssertEqual(t, coll.getFieldType("str").Kind.String(), "string") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("str").Type.String(), "string") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("str").Name, "Str") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("str").IsPointer, false) | ||||
| 	tst.AssertEqual(t, coll.getFieldValue(d, "str").(string), "3") | ||||
| 	tst.AssertEqual(t, gft("cdate").Kind.String(), "struct") | ||||
| 	tst.AssertEqual(t, gft("cdate").Type.String(), "time.Time") | ||||
| 	tst.AssertEqual(t, gft("cdate").Name, "CDate") | ||||
| 	tst.AssertEqual(t, gft("cdate").IsPointer, false) | ||||
| 	tst.AssertEqual(t, gfv("cdate").(time.Time), t0) | ||||
|  | ||||
| 	tst.AssertEqual(t, coll.getFieldType("ptr").Kind.String(), "int") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("ptr").Type.String(), "int") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("ptr").Name, "Ptr") | ||||
| 	tst.AssertEqual(t, coll.getFieldType("ptr").IsPointer, true) | ||||
| 	tst.AssertEqual(t, *coll.getFieldValue(d, "ptr").(*int), 4) | ||||
| 	tst.AssertEqual(t, gft("sub.a").Kind.String(), "string") | ||||
| 	tst.AssertEqual(t, gft("sub.a").Type.String(), "string") | ||||
| 	tst.AssertEqual(t, gft("sub.a").Name, "A") | ||||
| 	tst.AssertEqual(t, gft("sub.a").IsPointer, false) | ||||
| 	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) { | ||||
| @@ -177,3 +196,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) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user