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 bsoncodec
8
9import (
10	"reflect"
11
12	"go.mongodb.org/mongo-driver/bson/bsonoptions"
13	"go.mongodb.org/mongo-driver/bson/bsonrw"
14	"go.mongodb.org/mongo-driver/bson/bsontype"
15	"go.mongodb.org/mongo-driver/bson/primitive"
16)
17
18// EmptyInterfaceCodec is the Codec used for interface{} values.
19type EmptyInterfaceCodec struct {
20	DecodeBinaryAsSlice bool
21}
22
23var (
24	defaultEmptyInterfaceCodec = NewEmptyInterfaceCodec()
25
26	_ ValueCodec  = defaultEmptyInterfaceCodec
27	_ typeDecoder = defaultEmptyInterfaceCodec
28)
29
30// NewEmptyInterfaceCodec returns a EmptyInterfaceCodec with options opts.
31func NewEmptyInterfaceCodec(opts ...*bsonoptions.EmptyInterfaceCodecOptions) *EmptyInterfaceCodec {
32	interfaceOpt := bsonoptions.MergeEmptyInterfaceCodecOptions(opts...)
33
34	codec := EmptyInterfaceCodec{}
35	if interfaceOpt.DecodeBinaryAsSlice != nil {
36		codec.DecodeBinaryAsSlice = *interfaceOpt.DecodeBinaryAsSlice
37	}
38	return &codec
39}
40
41// EncodeValue is the ValueEncoderFunc for interface{}.
42func (eic EmptyInterfaceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
43	if !val.IsValid() || val.Type() != tEmpty {
44		return ValueEncoderError{Name: "EmptyInterfaceEncodeValue", Types: []reflect.Type{tEmpty}, Received: val}
45	}
46
47	if val.IsNil() {
48		return vw.WriteNull()
49	}
50	encoder, err := ec.LookupEncoder(val.Elem().Type())
51	if err != nil {
52		return err
53	}
54
55	return encoder.EncodeValue(ec, vw, val.Elem())
56}
57
58func (eic EmptyInterfaceCodec) getEmptyInterfaceDecodeType(dc DecodeContext, valueType bsontype.Type) (reflect.Type, error) {
59	isDocument := valueType == bsontype.Type(0) || valueType == bsontype.EmbeddedDocument
60	if isDocument && dc.Ancestor != nil {
61		// Using ancestor information rather than looking up the type map entry forces consistent decoding.
62		// If we're decoding into a bson.D, subdocuments should also be decoded as bson.D, even if a type map entry
63		// has been registered.
64		return dc.Ancestor, nil
65	}
66
67	rtype, err := dc.LookupTypeMapEntry(valueType)
68	if err == nil {
69		return rtype, nil
70	}
71
72	if isDocument {
73		// For documents, fallback to looking up a type map entry for bsontype.Type(0) or bsontype.EmbeddedDocument,
74		// depending on the original valueType.
75		var lookupType bsontype.Type
76		switch valueType {
77		case bsontype.Type(0):
78			lookupType = bsontype.EmbeddedDocument
79		case bsontype.EmbeddedDocument:
80			lookupType = bsontype.Type(0)
81		}
82
83		rtype, err = dc.LookupTypeMapEntry(lookupType)
84		if err == nil {
85			return rtype, nil
86		}
87	}
88
89	return nil, err
90}
91
92func (eic EmptyInterfaceCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
93	if t != tEmpty {
94		return emptyValue, ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.Zero(t)}
95	}
96
97	rtype, err := eic.getEmptyInterfaceDecodeType(dc, vr.Type())
98	if err != nil {
99		switch vr.Type() {
100		case bsontype.Null:
101			return reflect.Zero(t), vr.ReadNull()
102		default:
103			return emptyValue, err
104		}
105	}
106
107	decoder, err := dc.LookupDecoder(rtype)
108	if err != nil {
109		return emptyValue, err
110	}
111
112	elem, err := decodeTypeOrValue(decoder, dc, vr, rtype)
113	if err != nil {
114		return emptyValue, err
115	}
116
117	if eic.DecodeBinaryAsSlice && rtype == tBinary {
118		binElem := elem.Interface().(primitive.Binary)
119		if binElem.Subtype == bsontype.BinaryGeneric || binElem.Subtype == bsontype.BinaryBinaryOld {
120			elem = reflect.ValueOf(binElem.Data)
121		}
122	}
123
124	return elem, nil
125}
126
127// DecodeValue is the ValueDecoderFunc for interface{}.
128func (eic EmptyInterfaceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
129	if !val.CanSet() || val.Type() != tEmpty {
130		return ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: val}
131	}
132
133	elem, err := eic.decodeType(dc, vr, val.Type())
134	if err != nil {
135		return err
136	}
137
138	val.Set(elem)
139	return nil
140}
141