-
Notifications
You must be signed in to change notification settings - Fork 891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GODRIVER-2714 POC: Eliminate *Context
types used by BSON Encoder or Decoder.
#1612
Changes from all commits
9f587a2
d8fba65
1834500
dfdb9c3
394b263
51517bc
7938e90
b2a1c15
7a0b8bf
5ce2670
6ca8e11
9c32dc7
7f7a86b
e9b3bfa
757b0b2
c15afa8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,202 +72,33 @@ func (vde ValueDecoderError) Error() string { | |
return fmt.Sprintf("%s can only decode valid and settable %s, but got %s", vde.Name, strings.Join(typeKinds, ", "), received) | ||
} | ||
|
||
// EncodeContext is the contextual information required for a Codec to encode a | ||
// value. | ||
type EncodeContext struct { | ||
*Registry | ||
|
||
// MinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64, | ||
// uint, uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits) | ||
// that can represent the integer value. | ||
// | ||
// Deprecated: Use bson.Encoder.IntMinSize instead. | ||
MinSize bool | ||
|
||
errorOnInlineDuplicates bool | ||
stringifyMapKeysWithFmt bool | ||
nilMapAsEmpty bool | ||
nilSliceAsEmpty bool | ||
nilByteSliceAsEmpty bool | ||
omitZeroStruct bool | ||
useJSONStructTags bool | ||
} | ||
|
||
// ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in | ||
// the marshaled BSON when the "inline" struct tag option is set. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.ErrorOnInlineDuplicates] instead. | ||
func (ec *EncodeContext) ErrorOnInlineDuplicates() { | ||
ec.errorOnInlineDuplicates = true | ||
} | ||
|
||
// StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name | ||
// strings using fmt.Sprintf() instead of the default string conversion logic. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.StringifyMapKeysWithFmt] instead. | ||
func (ec *EncodeContext) StringifyMapKeysWithFmt() { | ||
ec.stringifyMapKeysWithFmt = true | ||
} | ||
|
||
// NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON | ||
// null. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilMapAsEmpty] instead. | ||
func (ec *EncodeContext) NilMapAsEmpty() { | ||
ec.nilMapAsEmpty = true | ||
} | ||
|
||
// NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON | ||
// null. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilSliceAsEmpty] instead. | ||
func (ec *EncodeContext) NilSliceAsEmpty() { | ||
ec.nilSliceAsEmpty = true | ||
} | ||
|
||
// NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values | ||
// instead of BSON null. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.NilByteSliceAsEmpty] instead. | ||
func (ec *EncodeContext) NilByteSliceAsEmpty() { | ||
ec.nilByteSliceAsEmpty = true | ||
} | ||
|
||
// OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{}) | ||
// as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set. | ||
// | ||
// Note that the Encoder only examines exported struct fields when determining if a struct is the | ||
// zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.OmitZeroStruct] instead. | ||
func (ec *EncodeContext) OmitZeroStruct() { | ||
ec.omitZeroStruct = true | ||
} | ||
|
||
// UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson" | ||
// struct tag is not specified. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Encoder.UseJSONStructTags] instead. | ||
func (ec *EncodeContext) UseJSONStructTags() { | ||
ec.useJSONStructTags = true | ||
} | ||
|
||
// DecodeContext is the contextual information required for a Codec to decode a | ||
// value. | ||
type DecodeContext struct { | ||
*Registry | ||
|
||
// Truncate, if true, instructs decoders to to truncate the fractional part of BSON "double" | ||
// values when attempting to unmarshal them into a Go integer (int, int8, int16, int32, int64, | ||
// uint, uint8, uint16, uint32, or uint64) struct field. The truncation logic does not apply to | ||
// BSON "decimal128" values. | ||
// | ||
// Deprecated: Use bson.Decoder.AllowTruncatingDoubles instead. | ||
Truncate bool | ||
|
||
// Ancestor is the type of a containing document. This is mainly used to determine what type | ||
// should be used when decoding an embedded document into an empty interface. For example, if | ||
// Ancestor is a bson.M, BSON embedded document values being decoded into an empty interface | ||
// will be decoded into a bson.M. | ||
// | ||
// Deprecated: Use bson.Decoder.DefaultDocumentM or bson.Decoder.DefaultDocumentD instead. | ||
Ancestor reflect.Type | ||
|
||
// defaultDocumentType specifies the Go type to decode top-level and nested BSON documents into. In particular, the | ||
// usage for this field is restricted to data typed as "interface{}" or "map[string]interface{}". If DocumentType is | ||
// set to a type that a BSON document cannot be unmarshaled into (e.g. "string"), unmarshalling will result in an | ||
// error. DocumentType overrides the Ancestor field. | ||
defaultDocumentType reflect.Type | ||
|
||
binaryAsSlice bool | ||
useJSONStructTags bool | ||
useLocalTimeZone bool | ||
zeroMaps bool | ||
zeroStructs bool | ||
} | ||
|
||
// BinaryAsSlice causes the Decoder to unmarshal BSON binary field values that are the "Generic" or | ||
// "Old" BSON binary subtype as a Go byte slice instead of a Binary. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.BinaryAsSlice] instead. | ||
func (dc *DecodeContext) BinaryAsSlice() { | ||
dc.binaryAsSlice = true | ||
} | ||
|
||
// UseJSONStructTags causes the Decoder to fall back to using the "json" struct tag if a "bson" | ||
// struct tag is not specified. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseJSONStructTags] instead. | ||
func (dc *DecodeContext) UseJSONStructTags() { | ||
dc.useJSONStructTags = true | ||
} | ||
|
||
// UseLocalTimeZone causes the Decoder to unmarshal time.Time values in the local timezone instead | ||
// of the UTC timezone. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.UseLocalTimeZone] instead. | ||
func (dc *DecodeContext) UseLocalTimeZone() { | ||
dc.useLocalTimeZone = true | ||
} | ||
|
||
// ZeroMaps causes the Decoder to delete any existing values from Go maps in the destination value | ||
// passed to Decode before unmarshaling BSON documents into them. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroMaps] instead. | ||
func (dc *DecodeContext) ZeroMaps() { | ||
dc.zeroMaps = true | ||
} | ||
|
||
// ZeroStructs causes the Decoder to delete any existing values from Go structs in the destination | ||
// value passed to Decode before unmarshaling BSON documents into them. | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.ZeroStructs] instead. | ||
func (dc *DecodeContext) ZeroStructs() { | ||
dc.zeroStructs = true | ||
} | ||
|
||
// DefaultDocumentM causes the Decoder to always unmarshal documents into the M type. This | ||
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}". | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentM] instead. | ||
func (dc *DecodeContext) DefaultDocumentM() { | ||
dc.defaultDocumentType = reflect.TypeOf(M{}) | ||
} | ||
|
||
// DefaultDocumentD causes the Decoder to always unmarshal documents into the D type. This | ||
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}". | ||
// | ||
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentD] instead. | ||
func (dc *DecodeContext) DefaultDocumentD() { | ||
dc.defaultDocumentType = reflect.TypeOf(D{}) | ||
} | ||
|
||
// ValueCodec is an interface for encoding and decoding a reflect.Value. | ||
// values. | ||
// | ||
// Deprecated: Use [ValueEncoder] and [ValueDecoder] instead. | ||
type ValueCodec interface { | ||
ValueEncoder | ||
ValueDecoder | ||
// 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. An | ||
// EncodeContext instance is provided to allow implementations to lookup further ValueEncoders and | ||
// to provide configuration information. | ||
// 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(EncodeContext, 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(EncodeContext, ValueWriter, reflect.Value) error | ||
type ValueEncoderFunc func(EncoderRegistry, ValueWriter, reflect.Value) error | ||
|
||
// EncodeValue implements the ValueEncoder interface. | ||
func (fn ValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error { | ||
return fn(ec, vw, val) | ||
func (fn ValueEncoderFunc) EncodeValue(reg EncoderRegistry, vw ValueWriter, val reflect.Value) error { | ||
return fn(reg, vw, val) | ||
} | ||
|
||
// DecoderRegistry is an interface provides a ValueDecoder based on the given reflect.Type. | ||
type DecoderRegistry interface { | ||
LookupDecoder(reflect.Type) (ValueDecoder, error) | ||
LookupTypeMapEntry(Type) (reflect.Type, error) | ||
} | ||
|
||
// ValueDecoder is the interface implemented by types that can decode BSON to a provided Go type. | ||
|
@@ -276,28 +107,28 @@ func (fn ValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val ref | |
// ValueDecoder. A DecodeContext instance is provided and serves similar functionality to the | ||
// EncodeContext. | ||
type ValueDecoder interface { | ||
DecodeValue(DecodeContext, ValueReader, reflect.Value) error | ||
DecodeValue(DecoderRegistry, ValueReader, reflect.Value) error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want to remove |
||
} | ||
|
||
// ValueDecoderFunc is an adapter function that allows a function with the correct signature to be | ||
// used as a ValueDecoder. | ||
type ValueDecoderFunc func(DecodeContext, ValueReader, reflect.Value) error | ||
type ValueDecoderFunc func(DecoderRegistry, ValueReader, reflect.Value) error | ||
|
||
// DecodeValue implements the ValueDecoder interface. | ||
func (fn ValueDecoderFunc) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error { | ||
return fn(dc, vr, val) | ||
func (fn ValueDecoderFunc) DecodeValue(reg DecoderRegistry, vr ValueReader, val reflect.Value) error { | ||
return fn(reg, vr, val) | ||
} | ||
|
||
// typeDecoder is the interface implemented by types that can handle the decoding of a value given its type. | ||
type typeDecoder interface { | ||
decodeType(DecodeContext, ValueReader, reflect.Type) (reflect.Value, error) | ||
decodeType(DecoderRegistry, ValueReader, reflect.Type) (reflect.Value, error) | ||
} | ||
|
||
// typeDecoderFunc is an adapter function that allows a function with the correct signature to be used as a typeDecoder. | ||
type typeDecoderFunc func(DecodeContext, ValueReader, reflect.Type) (reflect.Value, error) | ||
type typeDecoderFunc func(DecoderRegistry, ValueReader, reflect.Type) (reflect.Value, error) | ||
|
||
func (fn typeDecoderFunc) decodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) { | ||
return fn(dc, vr, t) | ||
func (fn typeDecoderFunc) decodeType(reg DecoderRegistry, vr ValueReader, t reflect.Type) (reflect.Value, error) { | ||
return fn(reg, vr, t) | ||
} | ||
|
||
// decodeAdapter allows two functions with the correct signatures to be used as both a ValueDecoder and typeDecoder. | ||
|
@@ -309,31 +140,13 @@ type decodeAdapter struct { | |
var _ ValueDecoder = decodeAdapter{} | ||
var _ typeDecoder = decodeAdapter{} | ||
|
||
// decodeTypeOrValue calls decoder.decodeType is decoder is a typeDecoder. Otherwise, it allocates a new element of type | ||
// t and calls decoder.DecodeValue on it. | ||
func decodeTypeOrValue(decoder ValueDecoder, dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) { | ||
td, _ := decoder.(typeDecoder) | ||
return decodeTypeOrValueWithInfo(decoder, td, dc, vr, t, true) | ||
} | ||
|
||
func decodeTypeOrValueWithInfo(vd ValueDecoder, td typeDecoder, dc DecodeContext, vr ValueReader, t reflect.Type, convert bool) (reflect.Value, error) { | ||
if td != nil { | ||
val, err := td.decodeType(dc, vr, t) | ||
if err == nil && convert && val.Type() != t { | ||
// This conversion step is necessary for slices and maps. If a user declares variables like: | ||
// | ||
// type myBool bool | ||
// var m map[string]myBool | ||
// | ||
// and tries to decode BSON bytes into the map, the decoding will fail if this conversion is not present | ||
// because we'll try to assign a value of type bool to one of type myBool. | ||
val = val.Convert(t) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ancestor type used by the empty interface codec is passed as the last argument of the |
||
} | ||
return val, err | ||
func decodeTypeOrValueWithInfo(vd ValueDecoder, reg DecoderRegistry, vr ValueReader, t reflect.Type) (reflect.Value, error) { | ||
if td, _ := vd.(typeDecoder); td != nil { | ||
return td.decodeType(reg, vr, t) | ||
} | ||
|
||
val := reflect.New(t).Elem() | ||
err := vd.DecodeValue(dc, vr, val) | ||
err := vd.DecodeValue(reg, vr, val) | ||
return val, err | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't want to remove
EncodeContext
from theValueEncoder
API, only from thebson.Encoder
API. KeepingEncodeContext
in theEncodeValue
function may significantly simplify this PR.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current
EncodeContext
/DecodeContext
is coupled with the fields inEncoder
/Decoder
and codecs. For example,OverwriteDuplicatedInlinedFields
exists in StructCodec, EncodeContext, and Encoder. Moreover, the example here,OverwriteDuplicatedInlinedFields
, is default true. Considering future non-zero default codec fields as well as the current use cases of codec configurations formgocompat
, using an empty struct, such astype structCodec struct {}
, and passingEncodeContext
/DecodeContext
to modify the encoding/decoding will be similarly complex. Meanwhile, the*Context
becomes a superset of all codec attributes since it has to be passed into different codecs. Also for custom codecs,*Context
is either hard to be extended or useless.Though
SetBehavior
andRegistry.SetCodecOption
are out of the scope of GODRIVER-2714, they also decouple the logic with individual codecs by distributing the configuration response to each codec, yet providing a single entry from outside.However, I agree that
SetBehavior
andRegistry.SetCodecOption
are redundant. We can makeRegistry.SetCodecOption
accessible to thebson
package only.A less radical PR keeping both
*Context
and codec fields is available at #1659.