summaryrefslogtreecommitdiffstats
path: root/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go')
-rw-r--r--vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go160
1 files changed, 124 insertions, 36 deletions
diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go
index 42e362b6ad..02b63bbe46 100644
--- a/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go
+++ b/vendor/go.mongodb.org/mongo-driver/bson/bsoncodec/registry.go
@@ -8,6 +8,7 @@ package bsoncodec
import (
"errors"
+ "fmt"
"reflect"
"sync"
@@ -115,15 +116,79 @@ func buildDefaultRegistry() *Registry {
// RegisterCodec will register the provided ValueCodec for the provided type.
func (rb *RegistryBuilder) RegisterCodec(t reflect.Type, codec ValueCodec) *RegistryBuilder {
- rb.RegisterEncoder(t, codec)
- rb.RegisterDecoder(t, codec)
+ rb.RegisterTypeEncoder(t, codec)
+ rb.RegisterTypeDecoder(t, codec)
return rb
}
-// RegisterEncoder will register the provided ValueEncoder to the provided type.
+// RegisterTypeEncoder will register the provided ValueEncoder for the provided type.
//
-// The type registered will be used directly, so an encoder can be registered for a type and a
-// different encoder can be registered for a pointer to that type.
+// The type will be used directly, so an encoder can be registered for a type and a different encoder can be registered
+// for a pointer to that type.
+//
+// If the given type is an interface, the encoder will be called when marshalling a type that is that interface. It
+// will not be called when marshalling a non-interface type that implements the interface.
+func (rb *RegistryBuilder) RegisterTypeEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder {
+ rb.typeEncoders[t] = enc
+ return rb
+}
+
+// RegisterHookEncoder will register an encoder for the provided interface type t. This encoder will be called when
+// marshalling a type if the type implements t or a pointer to the type implements t. If the provided type is not
+// an interface (i.e. t.Kind() != reflect.Interface), this method will panic.
+func (rb *RegistryBuilder) RegisterHookEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder {
+ if t.Kind() != reflect.Interface {
+ panicStr := fmt.Sprintf("RegisterHookEncoder expects a type with kind reflect.Interface, "+
+ "got type %s with kind %s", t, t.Kind())
+ panic(panicStr)
+ }
+
+ for idx, encoder := range rb.interfaceEncoders {
+ if encoder.i == t {
+ rb.interfaceEncoders[idx].ve = enc
+ return rb
+ }
+ }
+
+ rb.interfaceEncoders = append(rb.interfaceEncoders, interfaceValueEncoder{i: t, ve: enc})
+ return rb
+}
+
+// RegisterTypeDecoder will register the provided ValueDecoder for the provided type.
+//
+// The type will be used directly, so a decoder can be registered for a type and a different decoder can be registered
+// for a pointer to that type.
+//
+// If the given type is an interface, the decoder will be called when unmarshalling into a type that is that interface.
+// It will not be called when unmarshalling into a non-interface type that implements the interface.
+func (rb *RegistryBuilder) RegisterTypeDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder {
+ rb.typeDecoders[t] = dec
+ return rb
+}
+
+// RegisterHookDecoder will register an decoder for the provided interface type t. This decoder will be called when
+// unmarshalling into a type if the type implements t or a pointer to the type implements t. If the provided type is not
+// an interface (i.e. t.Kind() != reflect.Interface), this method will panic.
+func (rb *RegistryBuilder) RegisterHookDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder {
+ if t.Kind() != reflect.Interface {
+ panicStr := fmt.Sprintf("RegisterHookDecoder expects a type with kind reflect.Interface, "+
+ "got type %s with kind %s", t, t.Kind())
+ panic(panicStr)
+ }
+
+ for idx, decoder := range rb.interfaceDecoders {
+ if decoder.i == t {
+ rb.interfaceDecoders[idx].vd = dec
+ return rb
+ }
+ }
+
+ rb.interfaceDecoders = append(rb.interfaceDecoders, interfaceValueDecoder{i: t, vd: dec})
+ return rb
+}
+
+// RegisterEncoder has been deprecated and will be removed in a future major version release. Use RegisterTypeEncoder
+// or RegisterHookEncoder instead.
func (rb *RegistryBuilder) RegisterEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder {
if t == tEmpty {
rb.typeEncoders[t] = enc
@@ -145,10 +210,8 @@ func (rb *RegistryBuilder) RegisterEncoder(t reflect.Type, enc ValueEncoder) *Re
return rb
}
-// RegisterDecoder will register the provided ValueDecoder to the provided type.
-//
-// The type registered will be used directly, so a decoder can be registered for a type and a
-// different decoder can be registered for a pointer to that type.
+// RegisterDecoder has been deprecated and will be removed in a future major version release. Use RegisterTypeDecoder
+// or RegisterHookDecoder instead.
func (rb *RegistryBuilder) RegisterDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder {
if t == nil {
rb.typeDecoders[nil] = dec
@@ -192,11 +255,10 @@ func (rb *RegistryBuilder) RegisterDefaultDecoder(kind reflect.Kind, dec ValueDe
// mapping is decoding situations where an empty interface is used and a default type needs to be
// created and decoded into.
//
-// NOTE: It is unlikely that registering a type for BSON Embedded Document is actually desired. By
-// registering a type map entry for BSON Embedded Document the type registered will be used in any
-// case where a BSON Embedded Document will be decoded into an empty interface. For example, if you
-// register primitive.M, the EmptyInterface decoder will always use primitive.M, even if an ancestor
-// was a primitive.D.
+// By default, BSON documents will decode into interface{} values as bson.D. To change the default type for BSON
+// documents, a type map entry for bsontype.EmbeddedDocument should be registered. For example, to force BSON documents
+// to decode to bson.Raw, use the following code:
+// rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(bson.Raw{}))
func (rb *RegistryBuilder) RegisterTypeMapEntry(bt bsontype.Type, rt reflect.Type) *RegistryBuilder {
rb.typeMap[bt] = rt
return rb
@@ -240,11 +302,17 @@ func (rb *RegistryBuilder) Build() *Registry {
return registry
}
-// LookupEncoder will inspect the registry for an encoder that satisfies the
-// type provided. An encoder registered for a specific type will take
-// precedence over an encoder registered for an interface the type satisfies,
-// which takes precedence over an encoder for the reflect.Kind of the value. If
-// no encoder can be found, an error is returned.
+// LookupEncoder inspects the registry for an encoder for the given type. The lookup precendence works as follows:
+//
+// 1. An encoder registered for the exact type. If the given type represents an interface, an encoder registered using
+// RegisterTypeEncoder for the interface will be selected.
+//
+// 2. An encoder registered using RegisterHookEncoder for an interface implemented by the type or by a pointer to the
+// type.
+//
+// 3. An encoder registered for the reflect.Kind of the value.
+//
+// If no encoder is found, an error of type ErrNoEncoder is returned.
func (r *Registry) LookupEncoder(t reflect.Type) (ValueEncoder, error) {
encodererr := ErrNoEncoder{Type: t}
r.mu.RLock()
@@ -257,7 +325,7 @@ func (r *Registry) LookupEncoder(t reflect.Type) (ValueEncoder, error) {
return enc, nil
}
- enc, found = r.lookupInterfaceEncoder(t)
+ enc, found = r.lookupInterfaceEncoder(t, true)
if found {
r.mu.Lock()
r.typeEncoders[t] = enc
@@ -291,25 +359,38 @@ func (r *Registry) lookupTypeEncoder(t reflect.Type) (ValueEncoder, bool) {
return enc, found
}
-func (r *Registry) lookupInterfaceEncoder(t reflect.Type) (ValueEncoder, bool) {
+func (r *Registry) lookupInterfaceEncoder(t reflect.Type, allowAddr bool) (ValueEncoder, bool) {
if t == nil {
return nil, false
}
for _, ienc := range r.interfaceEncoders {
- if !t.Implements(ienc.i) {
- continue
+ if t.Implements(ienc.i) {
+ return ienc.ve, true
+ }
+ if allowAddr && t.Kind() != reflect.Ptr && reflect.PtrTo(t).Implements(ienc.i) {
+ // if *t implements an interface, this will catch if t implements an interface further ahead
+ // in interfaceEncoders
+ defaultEnc, found := r.lookupInterfaceEncoder(t, false)
+ if !found {
+ defaultEnc, _ = r.kindEncoders[t.Kind()]
+ }
+ return newCondAddrEncoder(ienc.ve, defaultEnc), true
}
-
- return ienc.ve, true
}
return nil, false
}
-// LookupDecoder will inspect the registry for a decoder that satisfies the
-// type provided. A decoder registered for a specific type will take
-// precedence over a decoder registered for an interface the type satisfies,
-// which takes precedence over a decoder for the reflect.Kind of the value. If
-// no decoder can be found, an error is returned.
+// LookupDecoder inspects the registry for an decoder for the given type. The lookup precendence works as follows:
+//
+// 1. A decoder registered for the exact type. If the given type represents an interface, a decoder registered using
+// RegisterTypeDecoder for the interface will be selected.
+//
+// 2. A decoder registered using RegisterHookDecoder for an interface implemented by the type or by a pointer to the
+// type.
+//
+// 3. A decoder registered for the reflect.Kind of the value.
+//
+// If no decoder is found, an error of type ErrNoDecoder is returned.
func (r *Registry) LookupDecoder(t reflect.Type) (ValueDecoder, error) {
if t == nil {
return nil, ErrNilType
@@ -325,7 +406,7 @@ func (r *Registry) LookupDecoder(t reflect.Type) (ValueDecoder, error) {
return dec, nil
}
- dec, found = r.lookupInterfaceDecoder(t)
+ dec, found = r.lookupInterfaceDecoder(t, true)
if found {
r.mu.Lock()
r.typeDecoders[t] = dec
@@ -352,13 +433,20 @@ func (r *Registry) lookupTypeDecoder(t reflect.Type) (ValueDecoder, bool) {
return dec, found
}
-func (r *Registry) lookupInterfaceDecoder(t reflect.Type) (ValueDecoder, bool) {
+func (r *Registry) lookupInterfaceDecoder(t reflect.Type, allowAddr bool) (ValueDecoder, bool) {
for _, idec := range r.interfaceDecoders {
- if !t.Implements(idec.i) && !reflect.PtrTo(t).Implements(idec.i) {
- continue
+ if t.Implements(idec.i) {
+ return idec.vd, true
+ }
+ if allowAddr && t.Kind() != reflect.Ptr && reflect.PtrTo(t).Implements(idec.i) {
+ // if *t implements an interface, this will catch if t implements an interface further ahead
+ // in interfaceDecoders
+ defaultDec, found := r.lookupInterfaceDecoder(t, false)
+ if !found {
+ defaultDec, _ = r.kindDecoders[t.Kind()]
+ }
+ return newCondAddrDecoder(idec.vd, defaultDec), true
}
-
- return idec.vd, true
}
return nil, false
}