1// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package mgocompat
8
9import (
10	"errors"
11	"reflect"
12	"time"
13
14	"go.mongodb.org/mongo-driver/bson"
15	"go.mongodb.org/mongo-driver/bson/bsoncodec"
16	"go.mongodb.org/mongo-driver/bson/bsonoptions"
17	"go.mongodb.org/mongo-driver/bson/bsontype"
18)
19
20var (
21	// ErrSetZero may be returned from a SetBSON method to have the value set to its respective zero value.
22	ErrSetZero = errors.New("set to zero")
23
24	tInt            = reflect.TypeOf(int(0))
25	tTime           = reflect.TypeOf(time.Time{})
26	tM              = reflect.TypeOf(bson.M{})
27	tInterfaceSlice = reflect.TypeOf([]interface{}{})
28	tByteSlice      = reflect.TypeOf([]byte{})
29	tEmpty          = reflect.TypeOf((*interface{})(nil)).Elem()
30	tGetter         = reflect.TypeOf((*Getter)(nil)).Elem()
31	tSetter         = reflect.TypeOf((*Setter)(nil)).Elem()
32)
33
34// Registry is the mgo compatible bsoncodec.Registry. It contains the default and
35// primitive codecs with mgo compatible options.
36var Registry = NewRegistryBuilder().Build()
37
38// RegistryRespectNilValues is the bsoncodec.Registry compatible with mgo withSetRespectNilValues set to true.
39var RegistryRespectNilValues = NewRespectNilValuesRegistryBuilder().Build()
40
41// NewRegistryBuilder creates a new bsoncodec.RegistryBuilder configured with the default encoders and
42// deocders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the
43// PrimitiveCodecs type in this package.
44func NewRegistryBuilder() *bsoncodec.RegistryBuilder {
45	rb := bsoncodec.NewRegistryBuilder()
46	bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
47	bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
48	bson.PrimitiveCodecs{}.RegisterPrimitiveCodecs(rb)
49
50	structcodec, _ := bsoncodec.NewStructCodec(bsoncodec.DefaultStructTagParser,
51		bsonoptions.StructCodec().
52			SetDecodeZeroStruct(true).
53			SetEncodeOmitDefaultStruct(true).
54			SetOverwriteDuplicatedInlinedFields(false).
55			SetAllowUnexportedFields(true))
56	emptyInterCodec := bsoncodec.NewEmptyInterfaceCodec(
57		bsonoptions.EmptyInterfaceCodec().
58			SetDecodeBinaryAsSlice(true))
59	mapCodec := bsoncodec.NewMapCodec(
60		bsonoptions.MapCodec().
61			SetDecodeZerosMap(true).
62			SetEncodeNilAsEmpty(true).
63			SetEncodeKeysWithStringer(true))
64	uintcodec := bsoncodec.NewUIntCodec(bsonoptions.UIntCodec().SetEncodeToMinSize(true))
65
66	rb.RegisterTypeDecoder(tEmpty, emptyInterCodec).
67		RegisterDefaultDecoder(reflect.String, bsoncodec.NewStringCodec(bsonoptions.StringCodec().SetDecodeObjectIDAsHex(false))).
68		RegisterDefaultDecoder(reflect.Struct, structcodec).
69		RegisterDefaultDecoder(reflect.Map, mapCodec).
70		RegisterTypeEncoder(tByteSlice, bsoncodec.NewByteSliceCodec(bsonoptions.ByteSliceCodec().SetEncodeNilAsEmpty(true))).
71		RegisterDefaultEncoder(reflect.Struct, structcodec).
72		RegisterDefaultEncoder(reflect.Slice, bsoncodec.NewSliceCodec(bsonoptions.SliceCodec().SetEncodeNilAsEmpty(true))).
73		RegisterDefaultEncoder(reflect.Map, mapCodec).
74		RegisterDefaultEncoder(reflect.Uint, uintcodec).
75		RegisterDefaultEncoder(reflect.Uint8, uintcodec).
76		RegisterDefaultEncoder(reflect.Uint16, uintcodec).
77		RegisterDefaultEncoder(reflect.Uint32, uintcodec).
78		RegisterDefaultEncoder(reflect.Uint64, uintcodec).
79		RegisterTypeMapEntry(bsontype.Int32, tInt).
80		RegisterTypeMapEntry(bsontype.DateTime, tTime).
81		RegisterTypeMapEntry(bsontype.Array, tInterfaceSlice).
82		RegisterTypeMapEntry(bsontype.Type(0), tM).
83		RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).
84		RegisterHookEncoder(tGetter, bsoncodec.ValueEncoderFunc(GetterEncodeValue)).
85		RegisterHookDecoder(tSetter, bsoncodec.ValueDecoderFunc(SetterDecodeValue))
86
87	return rb
88}
89
90// NewRespectNilValuesRegistryBuilder creates a new bsoncodec.RegistryBuilder configured to behave like mgo/bson
91// with RespectNilValues set to true.
92func NewRespectNilValuesRegistryBuilder() *bsoncodec.RegistryBuilder {
93	rb := NewRegistryBuilder()
94
95	structcodec, _ := bsoncodec.NewStructCodec(bsoncodec.DefaultStructTagParser,
96		bsonoptions.StructCodec().
97			SetDecodeZeroStruct(true).
98			SetEncodeOmitDefaultStruct(true).
99			SetOverwriteDuplicatedInlinedFields(false).
100			SetAllowUnexportedFields(true))
101	mapCodec := bsoncodec.NewMapCodec(
102		bsonoptions.MapCodec().
103			SetDecodeZerosMap(true).
104			SetEncodeNilAsEmpty(false))
105
106	rb.RegisterDefaultDecoder(reflect.Struct, structcodec).
107		RegisterDefaultDecoder(reflect.Map, mapCodec).
108		RegisterTypeEncoder(tByteSlice, bsoncodec.NewByteSliceCodec(bsonoptions.ByteSliceCodec().SetEncodeNilAsEmpty(false))).
109		RegisterDefaultEncoder(reflect.Struct, structcodec).
110		RegisterDefaultEncoder(reflect.Slice, bsoncodec.NewSliceCodec(bsonoptions.SliceCodec().SetEncodeNilAsEmpty(false))).
111		RegisterDefaultEncoder(reflect.Map, mapCodec)
112
113	return rb
114}
115