Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
qingyang-hu committed May 10, 2024
1 parent dfdb9c3 commit ec93f97
Show file tree
Hide file tree
Showing 38 changed files with 986 additions and 977 deletions.
2 changes: 1 addition & 1 deletion bson/array_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var (
)

// EncodeValue is the ValueEncoder for bsoncore.Array values.
func (ac *arrayCodec) EncodeValue(_ *Registry, vw ValueWriter, val reflect.Value) error {
func (ac *arrayCodec) EncodeValue(_ EncoderRegistry, vw ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tCoreArray {
return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
}
Expand Down
6 changes: 3 additions & 3 deletions bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,12 @@ func TestMapCodec(t *testing.T) {
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mapRegistry := NewRegistry()
mapRegistry.RegisterKindEncoder(reflect.Map, tc.codec)
mapRegistry := NewRegistryBuilder()
mapRegistry.RegisterKindEncoder(reflect.Map, func() ValueEncoder { return tc.codec })
buf := new(bytes.Buffer)
vw := NewValueWriter(buf)
enc := NewEncoder(vw)
enc.SetRegistry(mapRegistry)
enc.SetRegistry(mapRegistry.Build())
err := enc.Encode(mapObj)
assert.Nil(t, err, "Encode error: %v", err)
str := buf.String()
Expand Down
11 changes: 8 additions & 3 deletions bson/bsoncodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,21 +103,26 @@ type DecodeContext struct {
zeroStructs bool
}

// EncoderRegistry is an interface provides a ValueEncoder based on the given reflect.Type.
type EncoderRegistry interface {
LookupEncoder(reflect.Type) (ValueEncoder, error)
}

// ValueEncoder is the interface implemented by types that can encode a provided Go type to BSON.
// The value to encode is provided as a reflect.Value and a bson.ValueWriter is used within the
// EncodeValue method to actually create the BSON representation. For convenience, ValueEncoderFunc
// is provided to allow use of a function with the correct signature as a ValueEncoder. A pointer
// to a Registry instance is provided to allow implementations to lookup further ValueEncoders.
type ValueEncoder interface {
EncodeValue(*Registry, ValueWriter, reflect.Value) error
EncodeValue(EncoderRegistry, ValueWriter, reflect.Value) error
}

// ValueEncoderFunc is an adapter function that allows a function with the correct signature to be
// used as a ValueEncoder.
type ValueEncoderFunc func(*Registry, ValueWriter, reflect.Value) error
type ValueEncoderFunc func(EncoderRegistry, ValueWriter, reflect.Value) error

// EncodeValue implements the ValueEncoder interface.
func (fn ValueEncoderFunc) EncodeValue(reg *Registry, vw ValueWriter, val reflect.Value) error {
func (fn ValueEncoderFunc) EncodeValue(reg EncoderRegistry, vw ValueWriter, val reflect.Value) error {
return fn(reg, vw, val)
}

Expand Down
2 changes: 1 addition & 1 deletion bson/byte_slice_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var (
)

// EncodeValue is the ValueEncoder for []byte.
func (bsc *byteSliceCodec) EncodeValue(_ *Registry, vw ValueWriter, val reflect.Value) error {
func (bsc *byteSliceCodec) EncodeValue(_ EncoderRegistry, vw ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tByteSlice {
return ValueEncoderError{Name: "ByteSliceEncodeValue", Types: []reflect.Type{tByteSlice}, Received: val}
}
Expand Down
2 changes: 1 addition & 1 deletion bson/cond_addr_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type condAddrEncoder struct {
}

// EncodeValue is the ValueEncoderFunc for a value that may be addressable.
func (cae *condAddrEncoder) EncodeValue(reg *Registry, vw ValueWriter, val reflect.Value) error {
func (cae *condAddrEncoder) EncodeValue(reg EncoderRegistry, vw ValueWriter, val reflect.Value) error {
if val.CanAddr() {
return cae.canAddrEnc.EncodeValue(reg, vw, val)
}
Expand Down
4 changes: 2 additions & 2 deletions bson/cond_addr_codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ func TestCondAddrCodec(t *testing.T) {

t.Run("addressEncode", func(t *testing.T) {
invoked := 0
encode1 := ValueEncoderFunc(func(*Registry, ValueWriter, reflect.Value) error {
encode1 := ValueEncoderFunc(func(EncoderRegistry, ValueWriter, reflect.Value) error {
invoked = 1
return nil
})
encode2 := ValueEncoderFunc(func(*Registry, ValueWriter, reflect.Value) error {
encode2 := ValueEncoderFunc(func(EncoderRegistry, ValueWriter, reflect.Value) error {
invoked = 2
return nil
})
Expand Down
4 changes: 2 additions & 2 deletions bson/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestBasicDecode(t *testing.T) {

got := reflect.New(tc.sType).Elem()
vr := NewValueReader(tc.data)
reg := DefaultRegistry
reg := NewRegistryBuilder().Build()
decoder, err := reg.LookupDecoder(reflect.TypeOf(got))
noerr(t, err)
err = decoder.DecodeValue(DecodeContext{Registry: reg}, vr, got)
Expand Down Expand Up @@ -199,7 +199,7 @@ func TestDecoderv2(t *testing.T) {
t.Run("SetRegistry", func(t *testing.T) {
t.Parallel()

r1, r2 := DefaultRegistry, NewRegistry()
r1, r2 := DefaultRegistry, NewRegistryBuilder().Build()
dc1 := DecodeContext{Registry: r1}
dc2 := DecodeContext{Registry: r2}
dec := NewDecoder(NewValueReader([]byte{}))
Expand Down
2 changes: 1 addition & 1 deletion bson/default_value_decoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (d decodeBinaryError) Error() string {
// There is no support for decoding map[string]interface{} because there is no decoder for
// interface{}, so users must either register this decoder themselves or use the
// EmptyInterfaceDecoder available in the bson package.
func registerDefaultDecoders(reg *Registry) {
func registerDefaultDecoders(reg *RegistryBuilder) {
if reg == nil {
panic(errors.New("argument to RegisterDefaultDecoders must not be nil"))
}
Expand Down
89 changes: 47 additions & 42 deletions bson/default_value_decoders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ func TestDefaultValueDecoders(t *testing.T) {
{
"Lookup Error",
map[string]string{},
&DecodeContext{Registry: newTestRegistry()},
&DecodeContext{Registry: newTestRegistryBuilder().Build()},
&valueReaderWriter{},
readDocument,
ErrNoDecoder{Type: reflect.TypeOf("")},
Expand Down Expand Up @@ -905,7 +905,7 @@ func TestDefaultValueDecoders(t *testing.T) {
{
"Lookup Error",
[1]string{},
&DecodeContext{Registry: newTestRegistry()},
&DecodeContext{Registry: newTestRegistryBuilder().Build()},
&valueReaderWriter{BSONType: TypeArray},
readArray,
ErrNoDecoder{Type: reflect.TypeOf("")},
Expand Down Expand Up @@ -999,7 +999,7 @@ func TestDefaultValueDecoders(t *testing.T) {
{
"Lookup Error",
[]string{},
&DecodeContext{Registry: newTestRegistry()},
&DecodeContext{Registry: newTestRegistryBuilder().Build()},
&valueReaderWriter{BSONType: TypeArray},
readArray,
ErrNoDecoder{Type: reflect.TypeOf("")},
Expand Down Expand Up @@ -3310,7 +3310,7 @@ func TestDefaultValueDecoders(t *testing.T) {
t.Skip()
}
val := reflect.New(tEmpty).Elem()
dc := DecodeContext{Registry: newTestRegistry()}
dc := DecodeContext{Registry: newTestRegistryBuilder().Build()}
want := ErrNoTypeMapEntry{Type: tc.bsontype}
got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, val)
if !assert.CompareErrors(got, want) {
Expand All @@ -3323,8 +3323,9 @@ func TestDefaultValueDecoders(t *testing.T) {
t.Skip()
}
val := reflect.New(tEmpty).Elem()
reg := newTestRegistry()
reg.RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val))
reg := newTestRegistryBuilder().
RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
Build()
dc := DecodeContext{
Registry: reg,
}
Expand All @@ -3341,9 +3342,10 @@ func TestDefaultValueDecoders(t *testing.T) {
}
want := errors.New("DecodeValue failure error")
llc := &llCodec{t: t, err: want}
reg := newTestRegistry()
reg.RegisterTypeDecoder(reflect.TypeOf(tc.val), llc)
reg.RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val))
reg := newTestRegistryBuilder().
RegisterTypeDecoder(reflect.TypeOf(tc.val), llc).
RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
Build()
dc := DecodeContext{
Registry: reg,
}
Expand All @@ -3356,9 +3358,10 @@ func TestDefaultValueDecoders(t *testing.T) {
t.Run("Success", func(t *testing.T) {
want := tc.val
llc := &llCodec{t: t, decodeval: tc.val}
reg := newTestRegistry()
reg.RegisterTypeDecoder(reflect.TypeOf(tc.val), llc)
reg.RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val))
reg := newTestRegistryBuilder().
RegisterTypeDecoder(reflect.TypeOf(tc.val), llc).
RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
Build()
dc := DecodeContext{
Registry: reg,
}
Expand Down Expand Up @@ -3395,7 +3398,7 @@ func TestDefaultValueDecoders(t *testing.T) {
llvr := &valueReaderWriter{BSONType: TypeDouble}
want := ErrNoTypeMapEntry{Type: TypeDouble}
val := reflect.New(tEmpty).Elem()
got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: newTestRegistry()}, llvr, val)
got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: newTestRegistryBuilder().Build()}, llvr, val)
if !assert.CompareErrors(got, want) {
t.Errorf("Errors are not equal. got %v; want %v", got, want)
}
Expand All @@ -3416,15 +3419,15 @@ func TestDefaultValueDecoders(t *testing.T) {
// registering a custom type map entry for both Type(0) anad TypeEmbeddedDocument should cause
// both top-level and embedded documents to decode to registered type when unmarshalling to interface{}

topLevelReg := newTestRegistry()
registerDefaultEncoders(topLevelReg)
registerDefaultDecoders(topLevelReg)
topLevelReg.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))
topLevelRb := newTestRegistryBuilder()
registerDefaultEncoders(topLevelRb)
registerDefaultDecoders(topLevelRb)
topLevelRb.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))

embeddedReg := newTestRegistry()
registerDefaultEncoders(embeddedReg)
registerDefaultDecoders(embeddedReg)
embeddedReg.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))
embeddedRb := newTestRegistryBuilder()
registerDefaultEncoders(embeddedRb)
registerDefaultDecoders(embeddedRb)
embeddedRb.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))

// create doc {"nested": {"foo": 1}}
innerDoc := bsoncore.BuildDocument(
Expand All @@ -3445,8 +3448,8 @@ func TestDefaultValueDecoders(t *testing.T) {
name string
registry *Registry
}{
{"top level", topLevelReg},
{"embedded", embeddedReg},
{"top level", topLevelRb.Build()},
{"embedded", embeddedRb.Build()},
}
for _, tc := range testCases {
var got interface{}
Expand All @@ -3464,10 +3467,11 @@ func TestDefaultValueDecoders(t *testing.T) {
// If a type map entry is registered for TypeEmbeddedDocument, the decoder should use ancestor
// information if available instead of the registered entry.

reg := newTestRegistry()
registerDefaultEncoders(reg)
registerDefaultDecoders(reg)
reg.RegisterTypeMapEntry(TypeEmbeddedDocument, reflect.TypeOf(M{}))
rb := newTestRegistryBuilder()
registerDefaultEncoders(rb)
registerDefaultDecoders(rb)
rb.RegisterTypeMapEntry(TypeEmbeddedDocument, reflect.TypeOf(M{}))
reg := rb.Build()

// build document {"nested": {"foo": 10}}
inner := bsoncore.BuildDocument(
Expand Down Expand Up @@ -3500,8 +3504,9 @@ func TestDefaultValueDecoders(t *testing.T) {
emptyInterfaceErrorDecode := func(DecodeContext, ValueReader, reflect.Value) error {
return decodeValueError
}
emptyInterfaceErrorRegistry := newTestRegistry()
emptyInterfaceErrorRegistry.RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode))
emptyInterfaceErrorRegistry := newTestRegistryBuilder().
RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode)).
Build()

// Set up a document {foo: 10} and an error that would happen if the value were decoded into interface{}
// using the registry defined above.
Expand Down Expand Up @@ -3553,9 +3558,9 @@ func TestDefaultValueDecoders(t *testing.T) {
outerDoc := buildDocument(bsoncore.AppendDocumentElement(nil, "first", inner1Doc))

// Use a registry that has all default decoders with the custom interface{} decoder that always errors.
nestedRegistry := newTestRegistry()
registerDefaultDecoders(nestedRegistry)
nestedRegistry.RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode))
nestedRegistryBuilder := newTestRegistryBuilder()
registerDefaultDecoders(nestedRegistryBuilder)
nestedRegistryBuilder.RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode))
nestedErr := &DecodeError{
keys: []string{"fourth", "1", "third", "randomKey", "second", "first"},
wrapped: decodeValueError,
Expand Down Expand Up @@ -3640,15 +3645,15 @@ func TestDefaultValueDecoders(t *testing.T) {
"struct - no decoder found",
stringStruct{},
NewValueReader(docBytes),
newTestRegistry(),
newTestRegistryBuilder().Build(),
defaultTestStructCodec,
stringStructErr,
},
{
"deeply nested struct",
outer{},
NewValueReader(outerDoc),
nestedRegistry,
nestedRegistryBuilder.Build(),
defaultTestStructCodec,
nestedErr,
},
Expand Down Expand Up @@ -3705,11 +3710,11 @@ func TestDefaultValueDecoders(t *testing.T) {
bsoncore.BuildArrayElement(nil, "boolArray", trueValue),
)

reg := newTestRegistry()
registerDefaultDecoders(reg)
reg.RegisterTypeMapEntry(TypeBoolean, reflect.TypeOf(mybool(true)))
rb := newTestRegistryBuilder()
registerDefaultDecoders(rb)
rb.RegisterTypeMapEntry(TypeBoolean, reflect.TypeOf(mybool(true)))

dc := DecodeContext{Registry: reg}
dc := DecodeContext{Registry: rb.Build()}
vr := NewValueReader(docBytes)
val := reflect.New(tD).Elem()
err := dDecodeValue(dc, vr, val)
Expand Down Expand Up @@ -3774,8 +3779,8 @@ func buildDocument(elems []byte) []byte {
}

func buildDefaultRegistry() *Registry {
reg := newTestRegistry()
registerDefaultEncoders(reg)
registerDefaultDecoders(reg)
return reg
rb := newTestRegistryBuilder()
registerDefaultEncoders(rb)
registerDefaultDecoders(rb)
return rb.Build()
}
Loading

0 comments on commit ec93f97

Please sign in to comment.