diff --git a/benchmark/bson.go b/benchmark/bson.go index 4c06848a7c..2992fb3fad 100644 --- a/benchmark/bson.go +++ b/benchmark/bson.go @@ -12,7 +12,6 @@ import ( "path/filepath" "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/x/bsonx" ) const ( @@ -25,12 +24,12 @@ const ( // utility functions for the bson benchmarks -func loadSourceDocument(pathParts ...string) (bsonx.Doc, error) { +func loadSourceDocument(pathParts ...string) (bson.D, error) { data, err := ioutil.ReadFile(filepath.Join(pathParts...)) if err != nil { return nil, err } - doc := bsonx.Doc{} + var doc bson.D err = bson.UnmarshalExtJSON(data, true, &doc) if err != nil { return nil, err @@ -48,7 +47,7 @@ func loadSourceRaw(pathParts ...string) (bson.Raw, error) { if err != nil { return nil, err } - raw, err := doc.MarshalBSON() + raw, err := bson.Marshal(doc) if err != nil { return nil, err } diff --git a/benchmark/bson_document.go b/benchmark/bson_document.go index 1fa7e1f015..da25714e22 100644 --- a/benchmark/bson_document.go +++ b/benchmark/bson_document.go @@ -10,7 +10,7 @@ import ( "context" "errors" - "go.mongodb.org/mongo-driver/x/bsonx" + "go.mongodb.org/mongo-driver/bson" ) func bsonDocumentEncoding(tm TimerManager, iters int, source string) error { @@ -22,7 +22,7 @@ func bsonDocumentEncoding(tm TimerManager, iters int, source string) error { tm.ResetTimer() for i := 0; i < iters; i++ { - out, err := doc.MarshalBSON() + out, err := bson.Marshal(doc) if err != nil { return err } @@ -40,7 +40,7 @@ func bsonDocumentDecodingLazy(tm TimerManager, iters int, source string) error { return err } - raw, err := doc.MarshalBSON() + raw, err := bson.Marshal(doc) if err != nil { return err } @@ -48,7 +48,8 @@ func bsonDocumentDecodingLazy(tm TimerManager, iters int, source string) error { tm.ResetTimer() for i := 0; i < iters; i++ { - out, err := bsonx.ReadDoc(raw) + var out bson.D + err := bson.Unmarshal(raw, &out) if err != nil { return err } @@ -65,7 +66,7 @@ func bsonDocumentDecoding(tm TimerManager, iters, numKeys int, source string) er return err } - raw, err := doc.MarshalBSON() + raw, err := bson.Marshal(doc) if err != nil { return err } @@ -73,7 +74,8 @@ func bsonDocumentDecoding(tm TimerManager, iters, numKeys int, source string) er tm.ResetTimer() for i := 0; i < iters; i++ { - out, err := bsonx.ReadDoc(raw) + var out bson.D + err := bson.Unmarshal(raw, &out) if err != nil { return err } diff --git a/benchmark/multi.go b/benchmark/multi.go index 1a4faaa98e..ab2ff4b315 100644 --- a/benchmark/multi.go +++ b/benchmark/multi.go @@ -10,7 +10,7 @@ import ( "context" "errors" - "go.mongodb.org/mongo-driver/x/bsonx" + "go.mongodb.org/mongo-driver/bson" ) func MultiFindMany(ctx context.Context, tm TimerManager, iters int) error { @@ -46,7 +46,7 @@ func MultiFindMany(ctx context.Context, tm TimerManager, iters int) error { tm.ResetTimer() - cursor, err := coll.Find(ctx, bsonx.Doc{}) + cursor, err := coll.Find(ctx, bson.D{}) if err != nil { return err } @@ -103,7 +103,7 @@ func multiInsertCase(ctx context.Context, tm TimerManager, iters int, data strin return err } - err = db.RunCommand(ctx, bsonx.Doc{{"create", bsonx.String("corpus")}}).Err() + err = db.RunCommand(ctx, bson.D{{"create", "corpus"}}).Err() if err != nil { return err } diff --git a/benchmark/single.go b/benchmark/single.go index dfbe0903d9..b2a26a5c52 100644 --- a/benchmark/single.go +++ b/benchmark/single.go @@ -10,11 +10,11 @@ import ( "context" "errors" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/internal" "go.mongodb.org/mongo-driver/internal/testutil" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/x/bsonx" ) const ( @@ -51,17 +51,17 @@ func SingleRunCommand(ctx context.Context, tm TimerManager, iters int) error { } defer db.Client().Disconnect(ctx) - cmd := bsonx.Doc{{internal.LegacyHelloLowercase, bsonx.Boolean(true)}} + cmd := bson.D{{internal.LegacyHelloLowercase, true}} tm.ResetTimer() for i := 0; i < iters; i++ { - var doc bsonx.Doc + var doc bson.D err := db.RunCommand(ctx, cmd).Decode(&doc) if err != nil { return err } // read the document and then throw it away to prevent - out, err := doc.MarshalBSON() + out, err := bson.Marshal(doc) if err != nil { return err } @@ -93,23 +93,27 @@ func SingleFindOneByID(ctx context.Context, tm TimerManager, iters int) error { return err } coll := db.Collection("corpus") + for i := 0; i < iters; i++ { - id := int32(i) - res, err := coll.InsertOne(ctx, doc.Set("_id", bsonx.Int32(id))) + idDoc := make(bson.D, 0, len(doc)+1) + idDoc = append(idDoc, bson.E{"_id", i}) + idDoc = append(idDoc, doc...) + res, err := coll.InsertOne(ctx, idDoc) if err != nil { return err } if res.InsertedID == nil { - return errors.New("insert failed") + return errors.New("no inserted ID returned") } } tm.ResetTimer() for i := 0; i < iters; i++ { - res := coll.FindOne(ctx, bsonx.Doc{{"_id", bsonx.Int32(int32(i))}}) - if res == nil { - return errors.New("find one query produced nil result") + var res bson.D + err := coll.FindOne(ctx, bson.D{{"_id", i}}).Decode(&res) + if err != nil { + return err } } @@ -142,7 +146,7 @@ func singleInsertCase(ctx context.Context, tm TimerManager, iters int, data stri return err } - err = db.RunCommand(ctx, bsonx.Doc{{"create", bsonx.String("corpus")}}).Err() + err = db.RunCommand(ctx, bson.D{{"create", "corpus"}}).Err() if err != nil { return err } @@ -155,9 +159,6 @@ func singleInsertCase(ctx context.Context, tm TimerManager, iters int, data stri if _, err = coll.InsertOne(ctx, doc); err != nil { return err } - - // TODO: should be remove after resolving GODRIVER-468 - _ = doc.Delete("_id") } tm.StopTimer() diff --git a/mongo/cursor.go b/mongo/cursor.go index 99f8695152..f137c835c9 100644 --- a/mongo/cursor.go +++ b/mongo/cursor.go @@ -15,7 +15,6 @@ import ( "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/bsoncodec" - "go.mongodb.org/mongo-driver/x/bsonx" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/x/mongo/driver" "go.mongodb.org/mongo-driver/x/mongo/driver/session" @@ -83,8 +82,6 @@ func NewCursorFromDocuments(documents []interface{}, err error, registry *bsonco switch t := doc.(type) { case nil: return nil, ErrNilDocument - case bsonx.Doc: - doc = t.Copy() case []byte: // Slight optimization so we'll just use MarshalBSON and not go through the codec machinery. doc = bson.Raw(t) diff --git a/mongo/integration/collection_test.go b/mongo/integration/collection_test.go index 3cb3ba7cca..f277397d9e 100644 --- a/mongo/integration/collection_test.go +++ b/mongo/integration/collection_test.go @@ -20,7 +20,6 @@ import ( "go.mongodb.org/mongo-driver/mongo/integration/mtest" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/writeconcern" - "go.mongodb.org/mongo-driver/x/bsonx" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" ) @@ -337,7 +336,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) @@ -406,7 +405,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "index CreateOne error: %v", err) @@ -492,8 +491,6 @@ func TestCollection(t *testing.T) { doc := bson.D{{"$set", bson.D{{"x", 2}}}} docBytes, err := bson.Marshal(doc) assert.Nil(mt, err, "Marshal error: %v", err) - xUpdate := bsonx.Doc{{"x", bsonx.Int32(2)}} - xDoc := bsonx.Doc{{"$set", bsonx.Document(xUpdate)}} testCases := []struct { name string @@ -502,7 +499,6 @@ func TestCollection(t *testing.T) { {"bsoncore Document", bsoncore.Document(docBytes)}, {"bson Raw", bson.Raw(docBytes)}, {"bson D", doc}, - {"bsonx Document", xDoc}, {"byte slice", docBytes}, } for _, tc := range testCases { @@ -707,8 +703,8 @@ func TestCollection(t *testing.T) { assert.NotNil(mt, res.UpsertedID, "expected upserted ID, got nil") }) mt.Run("write error", func(mt *mtest.T) { - filter := bsonx.Doc{{"_id", bsonx.String("foo")}} - replacement := bsonx.Doc{{"_id", bsonx.Double(3.14159)}} + filter := bson.D{{"_id", "foo"}} + replacement := bson.D{{"_id", 3.14159}} _, err := mt.Coll.InsertOne(context.Background(), filter) assert.Nil(mt, err, "InsertOne error: %v", err) @@ -841,7 +837,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) @@ -1211,7 +1207,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) @@ -1271,7 +1267,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) @@ -1347,7 +1343,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) @@ -1429,7 +1425,7 @@ func TestCollection(t *testing.T) { initCollection(mt, mt.Coll) indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) @@ -1884,7 +1880,7 @@ func testAggregateWithOptions(mt *mtest.T, createIndex bool, opts *options.Aggre if createIndex { indexView := mt.Coll.Indexes() _, err := indexView.CreateOne(context.Background(), mongo.IndexModel{ - Keys: bsonx.Doc{{"x", bsonx.Int32(1)}}, + Keys: bson.D{{"x", 1}}, }) assert.Nil(mt, err, "CreateOne error: %v", err) } diff --git a/mongo/mongo.go b/mongo/mongo.go index 2fa5e54aee..372c6f548b 100644 --- a/mongo/mongo.go +++ b/mongo/mongo.go @@ -16,7 +16,6 @@ import ( "strings" "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/x/bsonx" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "go.mongodb.org/mongo-driver/bson" @@ -80,8 +79,6 @@ func transformAndEnsureID(registry *bsoncodec.Registry, val interface{}) (bsonco switch tt := val.(type) { case nil: return nil, nil, ErrNilDocument - case bsonx.Doc: - val = tt.Copy() case []byte: // Slight optimization so we'll just use MarshalBSON and not go through the codec machinery. val = bson.Raw(tt) @@ -264,7 +261,7 @@ func transformUpdateValue(registry *bsoncodec.Registry, update interface{}, doll switch t := update.(type) { case nil: return u, ErrNilDocument - case primitive.D, bsonx.Doc: + case primitive.D: u.Type = bsontype.EmbeddedDocument u.Data, err = transformBsoncoreDocument(registry, update, true, "update") if err != nil { diff --git a/x/bsonx/array.go b/x/bsonx/array.go deleted file mode 100644 index 80359e8c70..0000000000 --- a/x/bsonx/array.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx // import "go.mongodb.org/mongo-driver/x/bsonx" - -import ( - "bytes" - "errors" - "fmt" - "strconv" - - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -// ErrNilArray indicates that an operation was attempted on a nil *Array. -var ErrNilArray = errors.New("array is nil") - -// Arr represents an array in BSON. -type Arr []Val - -// String implements the fmt.Stringer interface. -func (a Arr) String() string { - var buf bytes.Buffer - buf.Write([]byte("bson.Array[")) - for idx, val := range a { - if idx > 0 { - buf.Write([]byte(", ")) - } - fmt.Fprintf(&buf, "%s", val) - } - buf.WriteByte(']') - - return buf.String() -} - -// MarshalBSONValue implements the bsoncodec.ValueMarshaler interface. -func (a Arr) MarshalBSONValue() (bsontype.Type, []byte, error) { - if a == nil { - // TODO: Should we do this? - return bsontype.Null, nil, nil - } - - idx, dst := bsoncore.ReserveLength(nil) - for idx, value := range a { - t, data, _ := value.MarshalBSONValue() // marshalBSONValue never returns an error. - dst = append(dst, byte(t)) - dst = append(dst, strconv.Itoa(idx)...) - dst = append(dst, 0x00) - dst = append(dst, data...) - } - dst = append(dst, 0x00) - dst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:]))) - return bsontype.Array, dst, nil -} - -// UnmarshalBSONValue implements the bsoncodec.ValueUnmarshaler interface. -func (a *Arr) UnmarshalBSONValue(t bsontype.Type, data []byte) error { - if a == nil { - return ErrNilArray - } - *a = (*a)[:0] - - elements, err := bsoncore.Document(data).Elements() - if err != nil { - return err - } - - for _, elem := range elements { - var val Val - rawval := elem.Value() - err = val.UnmarshalBSONValue(rawval.Type, rawval.Data) - if err != nil { - return err - } - *a = append(*a, val) - } - return nil -} - -// Equal compares this document to another, returning true if they are equal. -func (a Arr) Equal(a2 Arr) bool { - if len(a) != len(a2) { - return false - } - for idx := range a { - if !a[idx].Equal(a2[idx]) { - return false - } - } - return true -} - -func (Arr) idoc() {} diff --git a/x/bsonx/array_test.go b/x/bsonx/array_test.go deleted file mode 100644 index 27b323c8cf..0000000000 --- a/x/bsonx/array_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "fmt" -) - -func ExampleArray() { - internalVersion := "1234567" - - f := func(appName string) Arr { - arr := make(Arr, 0) - arr = append(arr, - Document(Doc{{"name", String("mongo-go-driver")}, {"version", String(internalVersion)}}), - Document(Doc{{"type", String("darwin")}, {"architecture", String("amd64")}}), - String("go1.9.2"), - ) - if appName != "" { - arr = append(arr, Document(MDoc{"name": String(appName)})) - } - - return arr - } - _, buf, err := f("hello-world").MarshalBSONValue() - if err != nil { - fmt.Println(err) - } - fmt.Println(buf) - - // Output: [154 0 0 0 3 48 0 52 0 0 0 2 110 97 109 101 0 16 0 0 0 109 111 110 103 111 45 103 111 45 100 114 105 118 101 114 0 2 118 101 114 115 105 111 110 0 8 0 0 0 49 50 51 52 53 54 55 0 0 3 49 0 46 0 0 0 2 116 121 112 101 0 7 0 0 0 100 97 114 119 105 110 0 2 97 114 99 104 105 116 101 99 116 117 114 101 0 6 0 0 0 97 109 100 54 52 0 0 2 50 0 8 0 0 0 103 111 49 46 57 46 50 0 3 51 0 27 0 0 0 2 110 97 109 101 0 12 0 0 0 104 101 108 108 111 45 119 111 114 108 100 0 0 0] -} diff --git a/x/bsonx/bson_test.go b/x/bsonx/bson_test.go deleted file mode 100644 index 35b4ae9cce..0000000000 --- a/x/bsonx/bson_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "testing" - - "go.mongodb.org/mongo-driver/bson/primitive" -) - -func noerr(t *testing.T, err error) { - if err != nil { - t.Helper() - t.Errorf("Unexpected error: (%T)%v", err, err) - t.FailNow() - } -} - -func compareDecimal128(d1, d2 primitive.Decimal128) bool { - d1H, d1L := d1.GetBytes() - d2H, d2L := d2.GetBytes() - - if d1H != d2H { - return false - } - - if d1L != d2L { - return false - } - - return true -} - -func compareErrors(err1, err2 error) bool { - if err1 == nil && err2 == nil { - return true - } - - if err1 == nil || err2 == nil { - return false - } - - if err1.Error() != err2.Error() { - return false - } - - return true -} diff --git a/x/bsonx/constructor.go b/x/bsonx/constructor.go deleted file mode 100644 index a8be859ddf..0000000000 --- a/x/bsonx/constructor.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "encoding/binary" - "math" - "time" - - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -// IDoc is the interface implemented by Doc and MDoc. It allows either of these types to be provided -// to the Document function to create a Value. -type IDoc interface { - idoc() -} - -// Double constructs a BSON double Value. -func Double(f64 float64) Val { - v := Val{t: bsontype.Double} - binary.LittleEndian.PutUint64(v.bootstrap[0:8], math.Float64bits(f64)) - return v -} - -// String constructs a BSON string Value. -func String(str string) Val { return Val{t: bsontype.String}.writestring(str) } - -// Document constructs a Value from the given IDoc. If nil is provided, a BSON Null value will be -// returned. -func Document(doc IDoc) Val { - var v Val - switch tt := doc.(type) { - case Doc: - if tt == nil { - v.t = bsontype.Null - break - } - v.t = bsontype.EmbeddedDocument - v.primitive = tt - case MDoc: - if tt == nil { - v.t = bsontype.Null - break - } - v.t = bsontype.EmbeddedDocument - v.primitive = tt - default: - v.t = bsontype.Null - } - return v -} - -// Array constructs a Value from arr. If arr is nil, a BSON Null value is returned. -func Array(arr Arr) Val { - if arr == nil { - return Val{t: bsontype.Null} - } - return Val{t: bsontype.Array, primitive: arr} -} - -// Binary constructs a BSON binary Value. -func Binary(subtype byte, data []byte) Val { - return Val{t: bsontype.Binary, primitive: primitive.Binary{Subtype: subtype, Data: data}} -} - -// Undefined constructs a BSON binary Value. -func Undefined() Val { return Val{t: bsontype.Undefined} } - -// ObjectID constructs a BSON objectid Value. -func ObjectID(oid primitive.ObjectID) Val { - v := Val{t: bsontype.ObjectID} - copy(v.bootstrap[0:12], oid[:]) - return v -} - -// Boolean constructs a BSON boolean Value. -func Boolean(b bool) Val { - v := Val{t: bsontype.Boolean} - if b { - v.bootstrap[0] = 0x01 - } - return v -} - -// DateTime constructs a BSON datetime Value. -func DateTime(dt int64) Val { return Val{t: bsontype.DateTime}.writei64(dt) } - -// Time constructs a BSON datetime Value. -func Time(t time.Time) Val { - return Val{t: bsontype.DateTime}.writei64(t.Unix()*1e3 + int64(t.Nanosecond()/1e6)) -} - -// Null constructs a BSON binary Value. -func Null() Val { return Val{t: bsontype.Null} } - -// Regex constructs a BSON regex Value. -func Regex(pattern, options string) Val { - regex := primitive.Regex{Pattern: pattern, Options: options} - return Val{t: bsontype.Regex, primitive: regex} -} - -// DBPointer constructs a BSON dbpointer Value. -func DBPointer(ns string, ptr primitive.ObjectID) Val { - dbptr := primitive.DBPointer{DB: ns, Pointer: ptr} - return Val{t: bsontype.DBPointer, primitive: dbptr} -} - -// JavaScript constructs a BSON javascript Value. -func JavaScript(js string) Val { - return Val{t: bsontype.JavaScript}.writestring(js) -} - -// Symbol constructs a BSON symbol Value. -func Symbol(symbol string) Val { - return Val{t: bsontype.Symbol}.writestring(symbol) -} - -// CodeWithScope constructs a BSON code with scope Value. -func CodeWithScope(code string, scope IDoc) Val { - cws := primitive.CodeWithScope{Code: primitive.JavaScript(code), Scope: scope} - return Val{t: bsontype.CodeWithScope, primitive: cws} -} - -// Int32 constructs a BSON int32 Value. -func Int32(i32 int32) Val { - v := Val{t: bsontype.Int32} - v.bootstrap[0] = byte(i32) - v.bootstrap[1] = byte(i32 >> 8) - v.bootstrap[2] = byte(i32 >> 16) - v.bootstrap[3] = byte(i32 >> 24) - return v -} - -// Timestamp constructs a BSON timestamp Value. -func Timestamp(t, i uint32) Val { - v := Val{t: bsontype.Timestamp} - v.bootstrap[0] = byte(i) - v.bootstrap[1] = byte(i >> 8) - v.bootstrap[2] = byte(i >> 16) - v.bootstrap[3] = byte(i >> 24) - v.bootstrap[4] = byte(t) - v.bootstrap[5] = byte(t >> 8) - v.bootstrap[6] = byte(t >> 16) - v.bootstrap[7] = byte(t >> 24) - return v -} - -// Int64 constructs a BSON int64 Value. -func Int64(i64 int64) Val { return Val{t: bsontype.Int64}.writei64(i64) } - -// Decimal128 constructs a BSON decimal128 Value. -func Decimal128(d128 primitive.Decimal128) Val { - return Val{t: bsontype.Decimal128, primitive: d128} -} - -// MinKey constructs a BSON minkey Value. -func MinKey() Val { return Val{t: bsontype.MinKey} } - -// MaxKey constructs a BSON maxkey Value. -func MaxKey() Val { return Val{t: bsontype.MaxKey} } diff --git a/x/bsonx/document.go b/x/bsonx/document.go deleted file mode 100644 index 2d53bc18b8..0000000000 --- a/x/bsonx/document.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "bytes" - "errors" - "fmt" - - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -// ErrNilDocument indicates that an operation was attempted on a nil *bson.Document. -var ErrNilDocument = errors.New("document is nil") - -// KeyNotFound is an error type returned from the Lookup methods on Document. This type contains -// information about which key was not found and if it was actually not found or if a component of -// the key except the last was not a document nor array. -type KeyNotFound struct { - Key []string // The keys that were searched for. - Depth uint // Which key either was not found or was an incorrect type. - Type bsontype.Type // The type of the key that was found but was an incorrect type. -} - -func (knf KeyNotFound) Error() string { - depth := knf.Depth - if depth >= uint(len(knf.Key)) { - depth = uint(len(knf.Key)) - 1 - } - - if len(knf.Key) == 0 { - return "no keys were provided for lookup" - } - - if knf.Type != bsontype.Type(0) { - return fmt.Sprintf(`key "%s" was found but was not valid to traverse BSON type %s`, knf.Key[depth], knf.Type) - } - - return fmt.Sprintf(`key "%s" was not found`, knf.Key[depth]) -} - -// Doc is a type safe, concise BSON document representation. -type Doc []Elem - -// ReadDoc will create a Document using the provided slice of bytes. If the -// slice of bytes is not a valid BSON document, this method will return an error. -func ReadDoc(b []byte) (Doc, error) { - doc := make(Doc, 0) - err := doc.UnmarshalBSON(b) - if err != nil { - return nil, err - } - return doc, nil -} - -// Copy makes a shallow copy of this document. -func (d Doc) Copy() Doc { - d2 := make(Doc, len(d)) - copy(d2, d) - return d2 -} - -// Append adds an element to the end of the document, creating it from the key and value provided. -func (d Doc) Append(key string, val Val) Doc { - return append(d, Elem{Key: key, Value: val}) -} - -// Prepend adds an element to the beginning of the document, creating it from the key and value provided. -func (d Doc) Prepend(key string, val Val) Doc { - // TODO: should we just modify d itself instead of doing an alloc here? - return append(Doc{{Key: key, Value: val}}, d...) -} - -// Set replaces an element of a document. If an element with a matching key is -// found, the element will be replaced with the one provided. If the document -// does not have an element with that key, the element is appended to the -// document instead. -func (d Doc) Set(key string, val Val) Doc { - idx := d.IndexOf(key) - if idx == -1 { - return append(d, Elem{Key: key, Value: val}) - } - d[idx] = Elem{Key: key, Value: val} - return d -} - -// IndexOf returns the index of the first element with a key of key, or -1 if no element with a key -// was found. -func (d Doc) IndexOf(key string) int { - for i, e := range d { - if e.Key == key { - return i - } - } - return -1 -} - -// Delete removes the element with key if it exists and returns the updated Doc. -func (d Doc) Delete(key string) Doc { - idx := d.IndexOf(key) - if idx == -1 { - return d - } - return append(d[:idx], d[idx+1:]...) -} - -// Lookup searches the document and potentially subdocuments or arrays for the -// provided key. Each key provided to this method represents a layer of depth. -// -// This method will return an empty Value if they key does not exist. To know if they key actually -// exists, use LookupErr. -func (d Doc) Lookup(key ...string) Val { - val, _ := d.LookupErr(key...) - return val -} - -// LookupErr searches the document and potentially subdocuments or arrays for the -// provided key. Each key provided to this method represents a layer of depth. -func (d Doc) LookupErr(key ...string) (Val, error) { - elem, err := d.LookupElementErr(key...) - return elem.Value, err -} - -// LookupElement searches the document and potentially subdocuments or arrays for the -// provided key. Each key provided to this method represents a layer of depth. -// -// This method will return an empty Element if they key does not exist. To know if they key actually -// exists, use LookupElementErr. -func (d Doc) LookupElement(key ...string) Elem { - elem, _ := d.LookupElementErr(key...) - return elem -} - -// LookupElementErr searches the document and potentially subdocuments for the -// provided key. Each key provided to this method represents a layer of depth. -func (d Doc) LookupElementErr(key ...string) (Elem, error) { - // KeyNotFound operates by being created where the error happens and then the depth is - // incremented by 1 as each function unwinds. Whenever this function returns, it also assigns - // the Key slice to the key slice it has. This ensures that the proper depth is identified and - // the proper keys. - if len(key) == 0 { - return Elem{}, KeyNotFound{Key: key} - } - - var elem Elem - var err error - idx := d.IndexOf(key[0]) - if idx == -1 { - return Elem{}, KeyNotFound{Key: key} - } - - elem = d[idx] - if len(key) == 1 { - return elem, nil - } - - switch elem.Value.Type() { - case bsontype.EmbeddedDocument: - switch tt := elem.Value.primitive.(type) { - case Doc: - elem, err = tt.LookupElementErr(key[1:]...) - case MDoc: - elem, err = tt.LookupElementErr(key[1:]...) - } - default: - return Elem{}, KeyNotFound{Type: elem.Value.Type()} - } - switch tt := err.(type) { - case KeyNotFound: - tt.Depth++ - tt.Key = key - return Elem{}, tt - case nil: - return elem, nil - default: - return Elem{}, err // We can't actually hit this. - } -} - -// MarshalBSONValue implements the bsoncodec.ValueMarshaler interface. -// -// This method will never return an error. -func (d Doc) MarshalBSONValue() (bsontype.Type, []byte, error) { - if d == nil { - // TODO: Should we do this? - return bsontype.Null, nil, nil - } - data, _ := d.MarshalBSON() - return bsontype.EmbeddedDocument, data, nil -} - -// MarshalBSON implements the Marshaler interface. -// -// This method will never return an error. -func (d Doc) MarshalBSON() ([]byte, error) { return d.AppendMarshalBSON(nil) } - -// AppendMarshalBSON marshals Doc to BSON bytes, appending to dst. -// -// This method will never return an error. -func (d Doc) AppendMarshalBSON(dst []byte) ([]byte, error) { - idx, dst := bsoncore.ReserveLength(dst) - for _, elem := range d { - t, data, _ := elem.Value.MarshalBSONValue() // Value.MarshalBSONValue never returns an error. - dst = append(dst, byte(t)) - dst = append(dst, elem.Key...) - dst = append(dst, 0x00) - dst = append(dst, data...) - } - dst = append(dst, 0x00) - dst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:]))) - return dst, nil -} - -// UnmarshalBSON implements the Unmarshaler interface. -func (d *Doc) UnmarshalBSON(b []byte) error { - if d == nil { - return ErrNilDocument - } - - if err := bsoncore.Document(b).Validate(); err != nil { - return err - } - - elems, err := bsoncore.Document(b).Elements() - if err != nil { - return err - } - var val Val - for _, elem := range elems { - rawv := elem.Value() - err = val.UnmarshalBSONValue(rawv.Type, rawv.Data) - if err != nil { - return err - } - *d = d.Append(elem.Key(), val) - } - return nil -} - -// UnmarshalBSONValue implements the bson.ValueUnmarshaler interface. -func (d *Doc) UnmarshalBSONValue(t bsontype.Type, data []byte) error { - if t != bsontype.EmbeddedDocument { - return fmt.Errorf("cannot unmarshal %s into a bsonx.Doc", t) - } - return d.UnmarshalBSON(data) -} - -// Equal compares this document to another, returning true if they are equal. -func (d Doc) Equal(id IDoc) bool { - switch tt := id.(type) { - case Doc: - d2 := tt - if len(d) != len(d2) { - return false - } - for idx := range d { - if !d[idx].Equal(d2[idx]) { - return false - } - } - case MDoc: - unique := make(map[string]struct{}) - for _, elem := range d { - unique[elem.Key] = struct{}{} - val, ok := tt[elem.Key] - if !ok { - return false - } - if !val.Equal(elem.Value) { - return false - } - } - if len(unique) != len(tt) { - return false - } - case nil: - return d == nil - default: - return false - } - - return true -} - -// String implements the fmt.Stringer interface. -func (d Doc) String() string { - var buf bytes.Buffer - buf.Write([]byte("bson.Document{")) - for idx, elem := range d { - if idx > 0 { - buf.Write([]byte(", ")) - } - fmt.Fprintf(&buf, "%v", elem) - } - buf.WriteByte('}') - - return buf.String() -} - -func (Doc) idoc() {} diff --git a/x/bsonx/document_test.go b/x/bsonx/document_test.go deleted file mode 100644 index 59c8cab5da..0000000000 --- a/x/bsonx/document_test.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "fmt" - "reflect" - "testing" - - "github.com/google/go-cmp/cmp" - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -func ExampleDocument() { - internalVersion := "1234567" - - f := func(appName string) Doc { - doc := Doc{ - {"driver", Document(Doc{{"name", String("mongo-go-driver")}, {"version", String(internalVersion)}})}, - {"os", Document(Doc{{"type", String("darwin")}, {"architecture", String("amd64")}})}, - {"platform", String("go1.11.1")}, - } - if appName != "" { - doc = append(doc, Elem{"application", Document(MDoc{"name": String(appName)})}) - } - - return doc - } - buf, err := f("hello-world").MarshalBSON() - if err != nil { - fmt.Println(err) - } - fmt.Println(buf) - - // Output: [178 0 0 0 3 100 114 105 118 101 114 0 52 0 0 0 2 110 97 109 101 0 16 0 0 0 109 111 110 103 111 45 103 111 45 100 114 105 118 101 114 0 2 118 101 114 115 105 111 110 0 8 0 0 0 49 50 51 52 53 54 55 0 0 3 111 115 0 46 0 0 0 2 116 121 112 101 0 7 0 0 0 100 97 114 119 105 110 0 2 97 114 99 104 105 116 101 99 116 117 114 101 0 6 0 0 0 97 109 100 54 52 0 0 2 112 108 97 116 102 111 114 109 0 9 0 0 0 103 111 49 46 49 49 46 49 0 3 97 112 112 108 105 99 97 116 105 111 110 0 27 0 0 0 2 110 97 109 101 0 12 0 0 0 104 101 108 108 111 45 119 111 114 108 100 0 0 0] -} - -func BenchmarkDocument(b *testing.B) { - b.ReportAllocs() - internalVersion := "1234567" - for i := 0; i < b.N; i++ { - doc := Doc{ - {"driver", Document(Doc{{"name", String("mongo-go-driver")}, {"version", String(internalVersion)}})}, - {"os", Document(Doc{{"type", String("darwin")}, {"architecture", String("amd64")}})}, - {"platform", String("go1.11.1")}, - } - _, _ = doc.MarshalBSON() - } -} - -func TestDocument(t *testing.T) { - t.Parallel() - t.Run("ReadDocument", func(t *testing.T) { - t.Parallel() - t.Run("UnmarshalingError", func(t *testing.T) { - t.Parallel() - testCases := []struct { - name string - invalid []byte - }{ - {"base", []byte{0x01, 0x02}}, - {"fuzzed1", []byte("0\x990\xc4")}, // fuzzed - {"fuzzed2", []byte("\x10\x00\x00\x00\x10\x000000\x0600\x00\x05\x00\xff\xff\xff\u007f")}, // fuzzed - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - want := bsoncore.NewInsufficientBytesError(nil, nil) - _, got := ReadDoc(tc.invalid) - if !compareErrors(got, want) { - t.Errorf("Expected errors to match. got %v; want %v", got, want) - } - }) - } - }) - t.Run("success", func(t *testing.T) { - t.Parallel() - valid := bsoncore.BuildDocument(nil, bsoncore.AppendNullElement(nil, "foobar")) - var want error - wantDoc := Doc{{"foobar", Null()}} - gotDoc, got := ReadDoc(valid) - if !compareErrors(got, want) { - t.Errorf("Expected errors to match. got %v; want %v", got, want) - } - if !cmp.Equal(gotDoc, wantDoc) { - t.Errorf("Expected returned documents to match. got %v; want %v", gotDoc, wantDoc) - } - }) - }) - t.Run("Copy", func(t *testing.T) { - t.Parallel() - testCases := []struct { - name string - start Doc - copy Doc - }{ - {"nil", nil, Doc{}}, - {"not-nil", Doc{{"foobar", Null()}}, Doc{{"foobar", Null()}}}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - copy := tc.start.Copy() - if !cmp.Equal(copy, tc.copy) { - t.Errorf("Expected copies to be equal. got %v; want %v", copy, tc.copy) - } - }) - } - }) - testCases := []struct { - name string - fn interface{} // method to call - params []interface{} // parameters - rets []interface{} // returns - }{ - { - "Append", Doc{}.Append, - []interface{}{"foo", Null()}, - []interface{}{Doc{{"foo", Null()}}}, - }, - { - "Prepend", Doc{}.Prepend, - []interface{}{"foo", Null()}, - []interface{}{Doc{{"foo", Null()}}}, - }, - { - "Set/append", Doc{{"foo", Null()}}.Set, - []interface{}{"bar", Null()}, - []interface{}{Doc{{"foo", Null()}, {"bar", Null()}}}, - }, - { - "Set/replace", Doc{{"foo", Null()}, {"bar", Null()}, {"baz", Double(3.14159)}}.Set, - []interface{}{"bar", Int64(1234567890)}, - []interface{}{Doc{{"foo", Null()}, {"bar", Int64(1234567890)}, {"baz", Double(3.14159)}}}, - }, - { - "Delete/doesn't exist", Doc{{"foo", Null()}, {"bar", Null()}, {"baz", Double(3.14159)}}.Delete, - []interface{}{"qux"}, - []interface{}{Doc{{"foo", Null()}, {"bar", Null()}, {"baz", Double(3.14159)}}}, - }, - { - "Delete/exists", Doc{{"foo", Null()}, {"bar", Null()}, {"baz", Double(3.14159)}}.Delete, - []interface{}{"bar"}, - []interface{}{Doc{{"foo", Null()}, {"baz", Double(3.14159)}}}, - }, - { - "Lookup/err", Doc{}.Lookup, - []interface{}{[]string{}}, - []interface{}{Val{}}, - }, - { - "Lookup/success", Doc{{"pi", Double(3.14159)}}.Lookup, - []interface{}{[]string{"pi"}}, - []interface{}{Double(3.14159)}, - }, - { - "LookupErr/err", Doc{}.LookupErr, - []interface{}{[]string{}}, - []interface{}{Val{}, KeyNotFound{Key: []string{}}}, - }, - { - "LookupErr/success", Doc{{"pi", Double(3.14159)}}.LookupErr, - []interface{}{[]string{"pi"}}, - []interface{}{Double(3.14159), error(nil)}, - }, - { - "LookupElem/err", Doc{}.LookupElement, - []interface{}{[]string{}}, - []interface{}{Elem{}}, - }, - { - "LookupElem/success", Doc{{"pi", Double(3.14159)}}.LookupElement, - []interface{}{[]string{"pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}}, - }, - { - "LookupElementErr/zero length key", Doc{}.LookupElementErr, - []interface{}{[]string{}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{}}}, - }, - { - "LookupElementErr/key not found", Doc{}.LookupElementErr, - []interface{}{[]string{"foo"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo"}}}, - }, - { - "LookupElementErr/key not found/depth 2", Doc{{"foo", Document(Doc{})}}.LookupElementErr, - []interface{}{[]string{"foo", "bar"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo", "bar"}, Depth: 1}}, - }, - { - "LookupElementErr/invalid depth 2 type", Doc{{"foo", Document(Doc{{"pi", Double(3.14159)}})}}.LookupElementErr, - []interface{}{[]string{"foo", "pi", "baz"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo", "pi", "baz"}, Depth: 1, Type: bsontype.Double}}, - }, - { - "LookupElementErr/success", Doc{{"pi", Double(3.14159)}}.LookupElementErr, - []interface{}{[]string{"pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}, error(nil)}, - }, - { - "LookupElementErr/success/depth 2", Doc{{"foo", Document(Doc{{"pi", Double(3.14159)}})}}.LookupElementErr, - []interface{}{[]string{"foo", "pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}, error(nil)}, - }, - { - "MarshalBSONValue/nil", Doc(nil).MarshalBSONValue, - nil, - []interface{}{bsontype.Null, []byte(nil), error(nil)}, - }, - { - "MarshalBSONValue/success", Doc{{"pi", Double(3.14159)}}.MarshalBSONValue, nil, - []interface{}{ - bsontype.EmbeddedDocument, - bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)), - error(nil), - }, - }, - { - "MarshalBSON", Doc{{"pi", Double(3.14159)}}.MarshalBSON, nil, - []interface{}{bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)), error(nil)}, - }, - { - "MarshalBSON/empty", Doc{}.MarshalBSON, nil, - []interface{}{bsoncore.BuildDocument(nil, nil), error(nil)}, - }, - { - "AppendMarshalBSON", Doc{{"pi", Double(3.14159)}}.AppendMarshalBSON, []interface{}{[]byte{0x01, 0x02, 0x03}}, - []interface{}{bsoncore.BuildDocument([]byte{0x01, 0x02, 0x03}, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)), error(nil)}, - }, - { - "AppendMarshalBSON/empty", Doc{}.AppendMarshalBSON, []interface{}{[]byte{0x01, 0x02, 0x03}}, - []interface{}{bsoncore.BuildDocument([]byte{0x01, 0x02, 0x03}, nil), error(nil)}, - }, - {"Equal/IDoc nil", Doc(nil).Equal, []interface{}{IDoc(nil)}, []interface{}{true}}, - {"Equal/MDoc nil", Doc(nil).Equal, []interface{}{MDoc(nil)}, []interface{}{true}}, - {"Equal/Doc/different length", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{Doc{}}, []interface{}{false}}, - {"Equal/Doc/elems not equal", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{Doc{{"pi", Int32(1)}}}, []interface{}{false}}, - {"Equal/Doc/success", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{Doc{{"pi", Double(3.14159)}}}, []interface{}{true}}, - {"Equal/MDoc/elems not equal", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{MDoc{"pi": Int32(1)}}, []interface{}{false}}, - {"Equal/MDoc/elems not found", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{MDoc{"foo": Int32(1)}}, []interface{}{false}}, - { - "Equal/MDoc/duplicate", - Doc{{"a", Int32(1)}, {"a", Int32(1)}}.Equal, []interface{}{MDoc{"a": Int32(1), "b": Int32(2)}}, - []interface{}{false}, - }, - {"Equal/MDoc/success", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{MDoc{"pi": Double(3.14159)}}, []interface{}{true}}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - fn := reflect.ValueOf(tc.fn) - if fn.Kind() != reflect.Func { - t.Fatalf("property fn must be a function, but it is a %v", fn.Kind()) - } - if fn.Type().NumIn() != len(tc.params) && !fn.Type().IsVariadic() { - t.Fatalf("number of parameters does not match. fn takes %d, but was provided %d", fn.Type().NumIn(), len(tc.params)) - } - params := make([]reflect.Value, 0, len(tc.params)) - for idx, param := range tc.params { - if param == nil { - params = append(params, reflect.New(fn.Type().In(idx)).Elem()) - continue - } - params = append(params, reflect.ValueOf(param)) - } - var rets []reflect.Value - if fn.Type().IsVariadic() { - rets = fn.CallSlice(params) - } else { - rets = fn.Call(params) - } - if len(rets) != len(tc.rets) { - t.Fatalf("mismatched number of returns. received %d; expected %d", len(rets), len(tc.rets)) - } - for idx := range rets { - got, want := rets[idx].Interface(), tc.rets[idx] - if !cmp.Equal(got, want) { - t.Errorf("Return %d does not match. got %v; want %v", idx, got, want) - } - } - }) - } -} diff --git a/x/bsonx/element.go b/x/bsonx/element.go deleted file mode 100644 index 00d1ba377f..0000000000 --- a/x/bsonx/element.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "fmt" - - "go.mongodb.org/mongo-driver/bson/bsontype" -) - -// ElementTypeError specifies that a method to obtain a BSON value an incorrect type was called on a bson.Value. -// -// TODO: rename this ValueTypeError. -type ElementTypeError struct { - Method string - Type bsontype.Type -} - -// Error implements the error interface. -func (ete ElementTypeError) Error() string { - return "Call of " + ete.Method + " on " + ete.Type.String() + " type" -} - -// Elem represents a BSON element. -// -// NOTE: Element cannot be the value of a map nor a property of a struct without special handling. -// The default encoders and decoders will not process Element correctly. To do so would require -// information loss since an Element contains a key, but the keys used when encoding a struct are -// the struct field names. Instead of using an Element, use a Value as a value in a map or a -// property of a struct. -type Elem struct { - Key string - Value Val -} - -// Equal compares e and e2 and returns true if they are equal. -func (e Elem) Equal(e2 Elem) bool { - if e.Key != e2.Key { - return false - } - return e.Value.Equal(e2.Value) -} - -func (e Elem) String() string { - // TODO(GODRIVER-612): When bsoncore has appenders for extended JSON use that here. - return fmt.Sprintf(`bson.Element{"%s": %v}`, e.Key, e.Value) -} diff --git a/x/bsonx/element_test.go b/x/bsonx/element_test.go deleted file mode 100644 index 41807403ed..0000000000 --- a/x/bsonx/element_test.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx diff --git a/x/bsonx/mdocument.go b/x/bsonx/mdocument.go deleted file mode 100644 index 7877f22401..0000000000 --- a/x/bsonx/mdocument.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "bytes" - "fmt" - - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -// MDoc is an unordered, type safe, concise BSON document representation. This type should not be -// used if you require ordering of values or duplicate keys. -type MDoc map[string]Val - -// ReadMDoc will create a Doc using the provided slice of bytes. If the -// slice of bytes is not a valid BSON document, this method will return an error. -func ReadMDoc(b []byte) (MDoc, error) { - doc := make(MDoc) - err := doc.UnmarshalBSON(b) - if err != nil { - return nil, err - } - return doc, nil -} - -// Copy makes a shallow copy of this document. -func (d MDoc) Copy() MDoc { - d2 := make(MDoc, len(d)) - for k, v := range d { - d2[k] = v - } - return d2 -} - -// Lookup searches the document and potentially subdocuments or arrays for the -// provided key. Each key provided to this method represents a layer of depth. -// -// This method will return an empty Value if they key does not exist. To know if they key actually -// exists, use LookupErr. -func (d MDoc) Lookup(key ...string) Val { - val, _ := d.LookupErr(key...) - return val -} - -// LookupErr searches the document and potentially subdocuments or arrays for the -// provided key. Each key provided to this method represents a layer of depth. -func (d MDoc) LookupErr(key ...string) (Val, error) { - elem, err := d.LookupElementErr(key...) - return elem.Value, err -} - -// LookupElement searches the document and potentially subdocuments or arrays for the -// provided key. Each key provided to this method represents a layer of depth. -// -// This method will return an empty Element if they key does not exist. To know if they key actually -// exists, use LookupElementErr. -func (d MDoc) LookupElement(key ...string) Elem { - elem, _ := d.LookupElementErr(key...) - return elem -} - -// LookupElementErr searches the document and potentially subdocuments for the -// provided key. Each key provided to this method represents a layer of depth. -func (d MDoc) LookupElementErr(key ...string) (Elem, error) { - // KeyNotFound operates by being created where the error happens and then the depth is - // incremented by 1 as each function unwinds. Whenever this function returns, it also assigns - // the Key slice to the key slice it has. This ensures that the proper depth is identified and - // the proper keys. - if len(key) == 0 { - return Elem{}, KeyNotFound{Key: key} - } - - var elem Elem - var err error - val, ok := d[key[0]] - if !ok { - return Elem{}, KeyNotFound{Key: key} - } - - if len(key) == 1 { - return Elem{Key: key[0], Value: val}, nil - } - - switch val.Type() { - case bsontype.EmbeddedDocument: - switch tt := val.primitive.(type) { - case Doc: - elem, err = tt.LookupElementErr(key[1:]...) - case MDoc: - elem, err = tt.LookupElementErr(key[1:]...) - } - default: - return Elem{}, KeyNotFound{Type: val.Type()} - } - switch tt := err.(type) { - case KeyNotFound: - tt.Depth++ - tt.Key = key - return Elem{}, tt - case nil: - return elem, nil - default: - return Elem{}, err // We can't actually hit this. - } -} - -// MarshalBSONValue implements the bsoncodec.ValueMarshaler interface. -// -// This method will never return an error. -func (d MDoc) MarshalBSONValue() (bsontype.Type, []byte, error) { - if d == nil { - // TODO: Should we do this? - return bsontype.Null, nil, nil - } - data, _ := d.MarshalBSON() - return bsontype.EmbeddedDocument, data, nil -} - -// MarshalBSON implements the Marshaler interface. -// -// This method will never return an error. -func (d MDoc) MarshalBSON() ([]byte, error) { return d.AppendMarshalBSON(nil) } - -// AppendMarshalBSON marshals Doc to BSON bytes, appending to dst. -// -// This method will never return an error. -func (d MDoc) AppendMarshalBSON(dst []byte) ([]byte, error) { - idx, dst := bsoncore.ReserveLength(dst) - for k, v := range d { - t, data, _ := v.MarshalBSONValue() // Value.MarshalBSONValue never returns an error. - dst = append(dst, byte(t)) - dst = append(dst, k...) - dst = append(dst, 0x00) - dst = append(dst, data...) - } - dst = append(dst, 0x00) - dst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:]))) - return dst, nil -} - -// UnmarshalBSON implements the Unmarshaler interface. -func (d *MDoc) UnmarshalBSON(b []byte) error { - if d == nil { - return ErrNilDocument - } - - if err := bsoncore.Document(b).Validate(); err != nil { - return err - } - - elems, err := bsoncore.Document(b).Elements() - if err != nil { - return err - } - var val Val - for _, elem := range elems { - rawv := elem.Value() - err = val.UnmarshalBSONValue(rawv.Type, rawv.Data) - if err != nil { - return err - } - (*d)[elem.Key()] = val - } - return nil -} - -// Equal compares this document to another, returning true if they are equal. -func (d MDoc) Equal(id IDoc) bool { - switch tt := id.(type) { - case MDoc: - d2 := tt - if len(d) != len(d2) { - return false - } - for key, value := range d { - value2, ok := d2[key] - if !ok { - return false - } - if !value.Equal(value2) { - return false - } - } - case Doc: - unique := make(map[string]struct{}) - for _, elem := range tt { - unique[elem.Key] = struct{}{} - val, ok := d[elem.Key] - if !ok { - return false - } - if !val.Equal(elem.Value) { - return false - } - } - if len(unique) != len(d) { - return false - } - case nil: - return d == nil - default: - return false - } - - return true -} - -// String implements the fmt.Stringer interface. -func (d MDoc) String() string { - var buf bytes.Buffer - buf.Write([]byte("bson.Document{")) - first := true - for key, value := range d { - if !first { - buf.Write([]byte(", ")) - } - fmt.Fprintf(&buf, "%v", Elem{Key: key, Value: value}) - first = false - } - buf.WriteByte('}') - - return buf.String() -} - -func (MDoc) idoc() {} diff --git a/x/bsonx/mdocument_test.go b/x/bsonx/mdocument_test.go deleted file mode 100644 index e3264d8001..0000000000 --- a/x/bsonx/mdocument_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "reflect" - "testing" - - "github.com/google/go-cmp/cmp" - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -func TestMDoc(t *testing.T) { - t.Parallel() - t.Run("ReadMDoc", func(t *testing.T) { - t.Parallel() - t.Run("UnmarshalingError", func(t *testing.T) { - t.Parallel() - invalid := []byte{0x01, 0x02} - want := bsoncore.NewInsufficientBytesError(nil, nil) - _, got := ReadMDoc(invalid) - if !compareErrors(got, want) { - t.Errorf("Expected errors to match. got %v; want %v", got, want) - } - }) - t.Run("success", func(t *testing.T) { - t.Parallel() - valid := bsoncore.BuildDocument(nil, bsoncore.AppendNullElement(nil, "foobar")) - var want error - wantDoc := MDoc{"foobar": Null()} - gotDoc, got := ReadMDoc(valid) - if !compareErrors(got, want) { - t.Errorf("Expected errors to match. got %v; want %v", got, want) - } - if !cmp.Equal(gotDoc, wantDoc) { - t.Errorf("Expected returned documents to match. got %v; want %v", gotDoc, wantDoc) - } - }) - }) - t.Run("Copy", func(t *testing.T) { - t.Parallel() - testCases := []struct { - name string - start MDoc - copy MDoc - }{ - {"nil", nil, MDoc{}}, - {"not-nil", MDoc{"foobar": Null()}, MDoc{"foobar": Null()}}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - copy := tc.start.Copy() - if !cmp.Equal(copy, tc.copy) { - t.Errorf("Expected copies to be equal. got %v; want %v", copy, tc.copy) - } - }) - } - }) - testCases := []struct { - name string - fn interface{} // method to call - params []interface{} // parameters - rets []interface{} // returns - }{ - { - "Lookup/err", MDoc{}.Lookup, - []interface{}{[]string{}}, - []interface{}{Val{}}, - }, - { - "Lookup/success", MDoc{"pi": Double(3.14159)}.Lookup, - []interface{}{[]string{"pi"}}, - []interface{}{Double(3.14159)}, - }, - { - "LookupErr/err", MDoc{}.LookupErr, - []interface{}{[]string{}}, - []interface{}{Val{}, KeyNotFound{Key: []string{}}}, - }, - { - "LookupErr/success", MDoc{"pi": Double(3.14159)}.LookupErr, - []interface{}{[]string{"pi"}}, - []interface{}{Double(3.14159), error(nil)}, - }, - { - "LookupElem/err", MDoc{}.LookupElement, - []interface{}{[]string{}}, - []interface{}{Elem{}}, - }, - { - "LookupElem/success", MDoc{"pi": Double(3.14159)}.LookupElement, - []interface{}{[]string{"pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}}, - }, - { - "LookupElementErr/zero length key", MDoc{}.LookupElementErr, - []interface{}{[]string{}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{}}}, - }, - { - "LookupElementErr/key not found", MDoc{}.LookupElementErr, - []interface{}{[]string{"foo"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo"}}}, - }, - { - "LookupElementErr/key not found/depth 2", MDoc{"foo": Document(Doc{})}.LookupElementErr, - []interface{}{[]string{"foo", "bar"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo", "bar"}, Depth: 1}}, - }, - { - "LookupElementErr/invalid depth 2 type", MDoc{"foo": Document(MDoc{"pi": Double(3.14159)})}.LookupElementErr, - []interface{}{[]string{"foo", "pi", "baz"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo", "pi", "baz"}, Depth: 1, Type: bsontype.Double}}, - }, - { - "LookupElementErr/invalid depth 2 type (Doc)", MDoc{"foo": Document(Doc{{"pi", Double(3.14159)}})}.LookupElementErr, - []interface{}{[]string{"foo", "pi", "baz"}}, - []interface{}{Elem{}, KeyNotFound{Key: []string{"foo", "pi", "baz"}, Depth: 1, Type: bsontype.Double}}, - }, - { - "LookupElementErr/success", MDoc{"pi": Double(3.14159)}.LookupElementErr, - []interface{}{[]string{"pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}, error(nil)}, - }, - { - "LookupElementErr/success/depth 2 (Doc)", MDoc{"foo": Document(Doc{{"pi", Double(3.14159)}})}.LookupElementErr, - []interface{}{[]string{"foo", "pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}, error(nil)}, - }, - { - "LookupElementErr/success/depth 2", MDoc{"foo": Document(MDoc{"pi": Double(3.14159)})}.LookupElementErr, - []interface{}{[]string{"foo", "pi"}}, - []interface{}{Elem{"pi", Double(3.14159)}, error(nil)}, - }, - { - "MarshalBSONValue/nil", MDoc(nil).MarshalBSONValue, - nil, - []interface{}{bsontype.Null, []byte(nil), error(nil)}, - }, - { - "MarshalBSONValue/success", MDoc{"pi": Double(3.14159)}.MarshalBSONValue, nil, - []interface{}{ - bsontype.EmbeddedDocument, - bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)), - error(nil), - }, - }, - { - "MarshalBSON", MDoc{"pi": Double(3.14159)}.MarshalBSON, nil, - []interface{}{bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)), error(nil)}, - }, - { - "MarshalBSON/empty", MDoc{}.MarshalBSON, nil, - []interface{}{bsoncore.BuildDocument(nil, nil), error(nil)}, - }, - { - "AppendMarshalBSON", MDoc{"pi": Double(3.14159)}.AppendMarshalBSON, []interface{}{[]byte{0x01, 0x02, 0x03}}, - []interface{}{bsoncore.BuildDocument([]byte{0x01, 0x02, 0x03}, bsoncore.AppendDoubleElement(nil, "pi", 3.14159)), error(nil)}, - }, - { - "AppendMarshalBSON/empty", MDoc{}.AppendMarshalBSON, []interface{}{[]byte{0x01, 0x02, 0x03}}, - []interface{}{bsoncore.BuildDocument([]byte{0x01, 0x02, 0x03}, nil), error(nil)}, - }, - {"Equal/IDoc nil", MDoc(nil).Equal, []interface{}{IDoc(nil)}, []interface{}{true}}, - {"Equal/MDoc nil", MDoc(nil).Equal, []interface{}{Doc(nil)}, []interface{}{true}}, - {"Equal/Doc/different length", MDoc{"pi": Double(3.14159)}.Equal, []interface{}{Doc{}}, []interface{}{false}}, - {"Equal/Doc/elems not equal", MDoc{"pi": Double(3.14159)}.Equal, []interface{}{Doc{{"pi", Int32(1)}}}, []interface{}{false}}, - {"Equal/Doc/success", MDoc{"pi": Double(3.14159)}.Equal, []interface{}{Doc{{"pi", Double(3.14159)}}}, []interface{}{true}}, - {"Equal/MDoc/elems not equal", MDoc{"pi": Double(3.14159)}.Equal, []interface{}{MDoc{"pi": Int32(1)}}, []interface{}{false}}, - {"Equal/MDoc/elems not found", MDoc{"pi": Double(3.14159)}.Equal, []interface{}{MDoc{"foo": Int32(1)}}, []interface{}{false}}, - { - "Equal/MDoc/duplicate", - Doc{{"a", Int32(1)}, {"a", Int32(1)}}.Equal, []interface{}{MDoc{"a": Int32(1), "b": Int32(2)}}, - []interface{}{false}, - }, - {"Equal/MDoc/success", Doc{{"pi", Double(3.14159)}}.Equal, []interface{}{MDoc{"pi": Double(3.14159)}}, []interface{}{true}}, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - fn := reflect.ValueOf(tc.fn) - if fn.Kind() != reflect.Func { - t.Fatalf("property fn must be a function, but it is a %v", fn.Kind()) - } - if fn.Type().NumIn() != len(tc.params) && !fn.Type().IsVariadic() { - t.Fatalf("number of parameters does not match. fn takes %d, but was provided %d", fn.Type().NumIn(), len(tc.params)) - } - params := make([]reflect.Value, 0, len(tc.params)) - for idx, param := range tc.params { - if param == nil { - params = append(params, reflect.New(fn.Type().In(idx)).Elem()) - continue - } - params = append(params, reflect.ValueOf(param)) - } - var rets []reflect.Value - if fn.Type().IsVariadic() { - rets = fn.CallSlice(params) - } else { - rets = fn.Call(params) - } - if len(rets) != len(tc.rets) { - t.Fatalf("mismatched number of returns. received %d; expected %d", len(rets), len(tc.rets)) - } - for idx := range rets { - got, want := rets[idx].Interface(), tc.rets[idx] - if !cmp.Equal(got, want) { - t.Errorf("Return %d does not match. got %v; want %v", idx, got, want) - } - } - }) - } -} diff --git a/x/bsonx/primitive_codecs.go b/x/bsonx/primitive_codecs.go deleted file mode 100644 index 01bd182678..0000000000 --- a/x/bsonx/primitive_codecs.go +++ /dev/null @@ -1,637 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "errors" - "fmt" - "reflect" - - "go.mongodb.org/mongo-driver/bson/bsoncodec" - "go.mongodb.org/mongo-driver/bson/bsonrw" - "go.mongodb.org/mongo-driver/bson/bsontype" -) - -var primitiveCodecs PrimitiveCodecs - -var tDocument = reflect.TypeOf((Doc)(nil)) -var tArray = reflect.TypeOf((Arr)(nil)) -var tValue = reflect.TypeOf(Val{}) -var tElementSlice = reflect.TypeOf(([]Elem)(nil)) - -// PrimitiveCodecs is a namespace for all of the default bsoncodec.Codecs for the primitive types -// defined in this package. -type PrimitiveCodecs struct{} - -// RegisterPrimitiveCodecs will register the encode and decode methods attached to PrimitiveCodecs -// with the provided RegistryBuilder. if rb is nil, a new empty RegistryBuilder will be created. -func (pc PrimitiveCodecs) RegisterPrimitiveCodecs(rb *bsoncodec.RegistryBuilder) { - if rb == nil { - panic(errors.New("argument to RegisterPrimitiveCodecs must not be nil")) - } - - rb. - RegisterTypeEncoder(tDocument, bsoncodec.ValueEncoderFunc(pc.DocumentEncodeValue)). - RegisterTypeEncoder(tArray, bsoncodec.ValueEncoderFunc(pc.ArrayEncodeValue)). - RegisterTypeEncoder(tValue, bsoncodec.ValueEncoderFunc(pc.ValueEncodeValue)). - RegisterTypeEncoder(tElementSlice, bsoncodec.ValueEncoderFunc(pc.ElementSliceEncodeValue)). - RegisterTypeDecoder(tDocument, bsoncodec.ValueDecoderFunc(pc.DocumentDecodeValue)). - RegisterTypeDecoder(tArray, bsoncodec.ValueDecoderFunc(pc.ArrayDecodeValue)). - RegisterTypeDecoder(tValue, bsoncodec.ValueDecoderFunc(pc.ValueDecodeValue)). - RegisterTypeDecoder(tElementSlice, bsoncodec.ValueDecoderFunc(pc.ElementSliceDecodeValue)) -} - -// DocumentEncodeValue is the ValueEncoderFunc for *Document. -func (pc PrimitiveCodecs) DocumentEncodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { - if !val.IsValid() || val.Type() != tDocument { - return bsoncodec.ValueEncoderError{Name: "DocumentEncodeValue", Types: []reflect.Type{tDocument}, Received: val} - } - - if val.IsNil() { - return vw.WriteNull() - } - - doc := val.Interface().(Doc) - - dw, err := vw.WriteDocument() - if err != nil { - return err - } - - return pc.encodeDocument(ec, dw, doc) -} - -// DocumentDecodeValue is the ValueDecoderFunc for *Document. -func (pc PrimitiveCodecs) DocumentDecodeValue(dctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { - if !val.CanSet() || val.Type() != tDocument { - return bsoncodec.ValueDecoderError{Name: "DocumentDecodeValue", Types: []reflect.Type{tDocument}, Received: val} - } - - return pc.documentDecodeValue(dctx, vr, val.Addr().Interface().(*Doc)) -} - -func (pc PrimitiveCodecs) documentDecodeValue(dctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, doc *Doc) error { - - dr, err := vr.ReadDocument() - if err != nil { - return err - } - - return pc.decodeDocument(dctx, dr, doc) -} - -// ArrayEncodeValue is the ValueEncoderFunc for *Array. -func (pc PrimitiveCodecs) ArrayEncodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { - if !val.IsValid() || val.Type() != tArray { - return bsoncodec.ValueEncoderError{Name: "ArrayEncodeValue", Types: []reflect.Type{tArray}, Received: val} - } - - if val.IsNil() { - return vw.WriteNull() - } - - arr := val.Interface().(Arr) - - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - dvw, err := aw.WriteArrayElement() - if err != nil { - return err - } - - err = pc.encodeValue(ec, dvw, val) - - if err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -// ArrayDecodeValue is the ValueDecoderFunc for *Array. -func (pc PrimitiveCodecs) ArrayDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { - if !val.CanSet() || val.Type() != tArray { - return bsoncodec.ValueDecoderError{Name: "ArrayDecodeValue", Types: []reflect.Type{tArray}, Received: val} - } - - ar, err := vr.ReadArray() - if err != nil { - return err - } - - if val.IsNil() { - val.Set(reflect.MakeSlice(tArray, 0, 0)) - } - val.SetLen(0) - - for { - vr, err := ar.ReadValue() - if err == bsonrw.ErrEOA { - break - } - if err != nil { - return err - } - - var elem Val - err = pc.valueDecodeValue(dc, vr, &elem) - if err != nil { - return err - } - - val.Set(reflect.Append(val, reflect.ValueOf(elem))) - } - - return nil -} - -// ElementSliceEncodeValue is the ValueEncoderFunc for []*Element. -func (pc PrimitiveCodecs) ElementSliceEncodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { - if !val.IsValid() || val.Type() != tElementSlice { - return bsoncodec.ValueEncoderError{Name: "ElementSliceEncodeValue", Types: []reflect.Type{tElementSlice}, Received: val} - } - - if val.IsNil() { - return vw.WriteNull() - } - - return pc.DocumentEncodeValue(ec, vw, val.Convert(tDocument)) -} - -// ElementSliceDecodeValue is the ValueDecoderFunc for []*Element. -func (pc PrimitiveCodecs) ElementSliceDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { - if !val.CanSet() || val.Type() != tElementSlice { - return bsoncodec.ValueDecoderError{Name: "ElementSliceDecodeValue", Types: []reflect.Type{tElementSlice}, Received: val} - } - - if val.IsNil() { - val.Set(reflect.MakeSlice(val.Type(), 0, 0)) - } - - val.SetLen(0) - - dr, err := vr.ReadDocument() - if err != nil { - return err - } - elems := make([]reflect.Value, 0) - for { - key, vr, err := dr.ReadElement() - if err == bsonrw.ErrEOD { - break - } - if err != nil { - return err - } - - var elem Elem - err = pc.elementDecodeValue(dc, vr, key, &elem) - if err != nil { - return err - } - - elems = append(elems, reflect.ValueOf(elem)) - } - - val.Set(reflect.Append(val, elems...)) - return nil -} - -// ValueEncodeValue is the ValueEncoderFunc for *Value. -func (pc PrimitiveCodecs) ValueEncodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { - if !val.IsValid() || val.Type() != tValue { - return bsoncodec.ValueEncoderError{Name: "ValueEncodeValue", Types: []reflect.Type{tValue}, Received: val} - } - - v := val.Interface().(Val) - - return pc.encodeValue(ec, vw, v) -} - -// ValueDecodeValue is the ValueDecoderFunc for *Value. -func (pc PrimitiveCodecs) ValueDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { - if !val.CanSet() || val.Type() != tValue { - return bsoncodec.ValueDecoderError{Name: "ValueDecodeValue", Types: []reflect.Type{tValue}, Received: val} - } - - return pc.valueDecodeValue(dc, vr, val.Addr().Interface().(*Val)) -} - -// encodeDocument is a separate function that we use because CodeWithScope -// returns us a DocumentWriter and we need to do the same logic that we would do -// for a document but cannot use a Codec. -func (pc PrimitiveCodecs) encodeDocument(ec bsoncodec.EncodeContext, dw bsonrw.DocumentWriter, doc Doc) error { - for _, elem := range doc { - dvw, err := dw.WriteDocumentElement(elem.Key) - if err != nil { - return err - } - - err = pc.encodeValue(ec, dvw, elem.Value) - - if err != nil { - return err - } - } - - return dw.WriteDocumentEnd() -} - -// DecodeDocument haves decoding into a Doc from a bsonrw.DocumentReader. -func (pc PrimitiveCodecs) DecodeDocument(dctx bsoncodec.DecodeContext, dr bsonrw.DocumentReader, pdoc *Doc) error { - return pc.decodeDocument(dctx, dr, pdoc) -} - -func (pc PrimitiveCodecs) decodeDocument(dctx bsoncodec.DecodeContext, dr bsonrw.DocumentReader, pdoc *Doc) error { - if *pdoc == nil { - *pdoc = make(Doc, 0) - } - *pdoc = (*pdoc)[:0] - for { - key, vr, err := dr.ReadElement() - if err == bsonrw.ErrEOD { - break - } - if err != nil { - return err - } - - var elem Elem - err = pc.elementDecodeValue(dctx, vr, key, &elem) - if err != nil { - return err - } - - *pdoc = append(*pdoc, elem) - } - return nil -} - -func (pc PrimitiveCodecs) elementDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, key string, elem *Elem) error { - var val Val - switch vr.Type() { - case bsontype.Double: - f64, err := vr.ReadDouble() - if err != nil { - return err - } - val = Double(f64) - case bsontype.String: - str, err := vr.ReadString() - if err != nil { - return err - } - val = String(str) - case bsontype.EmbeddedDocument: - var embeddedDoc Doc - err := pc.documentDecodeValue(dc, vr, &embeddedDoc) - if err != nil { - return err - } - val = Document(embeddedDoc) - case bsontype.Array: - arr := reflect.New(tArray).Elem() - err := pc.ArrayDecodeValue(dc, vr, arr) - if err != nil { - return err - } - val = Array(arr.Interface().(Arr)) - case bsontype.Binary: - data, subtype, err := vr.ReadBinary() - if err != nil { - return err - } - val = Binary(subtype, data) - case bsontype.Undefined: - err := vr.ReadUndefined() - if err != nil { - return err - } - val = Undefined() - case bsontype.ObjectID: - oid, err := vr.ReadObjectID() - if err != nil { - return err - } - val = ObjectID(oid) - case bsontype.Boolean: - b, err := vr.ReadBoolean() - if err != nil { - return err - } - val = Boolean(b) - case bsontype.DateTime: - dt, err := vr.ReadDateTime() - if err != nil { - return err - } - val = DateTime(dt) - case bsontype.Null: - err := vr.ReadNull() - if err != nil { - return err - } - val = Null() - case bsontype.Regex: - pattern, options, err := vr.ReadRegex() - if err != nil { - return err - } - val = Regex(pattern, options) - case bsontype.DBPointer: - ns, pointer, err := vr.ReadDBPointer() - if err != nil { - return err - } - val = DBPointer(ns, pointer) - case bsontype.JavaScript: - js, err := vr.ReadJavascript() - if err != nil { - return err - } - val = JavaScript(js) - case bsontype.Symbol: - symbol, err := vr.ReadSymbol() - if err != nil { - return err - } - val = Symbol(symbol) - case bsontype.CodeWithScope: - code, scope, err := vr.ReadCodeWithScope() - if err != nil { - return err - } - var doc Doc - err = pc.decodeDocument(dc, scope, &doc) - if err != nil { - return err - } - val = CodeWithScope(code, doc) - case bsontype.Int32: - i32, err := vr.ReadInt32() - if err != nil { - return err - } - val = Int32(i32) - case bsontype.Timestamp: - t, i, err := vr.ReadTimestamp() - if err != nil { - return err - } - val = Timestamp(t, i) - case bsontype.Int64: - i64, err := vr.ReadInt64() - if err != nil { - return err - } - val = Int64(i64) - case bsontype.Decimal128: - d128, err := vr.ReadDecimal128() - if err != nil { - return err - } - val = Decimal128(d128) - case bsontype.MinKey: - err := vr.ReadMinKey() - if err != nil { - return err - } - val = MinKey() - case bsontype.MaxKey: - err := vr.ReadMaxKey() - if err != nil { - return err - } - val = MaxKey() - default: - return fmt.Errorf("Cannot read unknown BSON type %s", vr.Type()) - } - - *elem = Elem{Key: key, Value: val} - return nil -} - -// encodeValue does not validation, and the callers must perform validation on val before calling -// this method. -func (pc PrimitiveCodecs) encodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val Val) error { - var err error - switch val.Type() { - case bsontype.Double: - err = vw.WriteDouble(val.Double()) - case bsontype.String: - err = vw.WriteString(val.StringValue()) - case bsontype.EmbeddedDocument: - var encoder bsoncodec.ValueEncoder - encoder, err = ec.LookupEncoder(tDocument) - if err != nil { - break - } - err = encoder.EncodeValue(ec, vw, reflect.ValueOf(val.Document())) - case bsontype.Array: - var encoder bsoncodec.ValueEncoder - encoder, err = ec.LookupEncoder(tArray) - if err != nil { - break - } - err = encoder.EncodeValue(ec, vw, reflect.ValueOf(val.Array())) - case bsontype.Binary: - // TODO: FIX THIS (╯°□°)╯︵ ┻━┻ - subtype, data := val.Binary() - err = vw.WriteBinaryWithSubtype(data, subtype) - case bsontype.Undefined: - err = vw.WriteUndefined() - case bsontype.ObjectID: - err = vw.WriteObjectID(val.ObjectID()) - case bsontype.Boolean: - err = vw.WriteBoolean(val.Boolean()) - case bsontype.DateTime: - err = vw.WriteDateTime(val.DateTime()) - case bsontype.Null: - err = vw.WriteNull() - case bsontype.Regex: - err = vw.WriteRegex(val.Regex()) - case bsontype.DBPointer: - err = vw.WriteDBPointer(val.DBPointer()) - case bsontype.JavaScript: - err = vw.WriteJavascript(val.JavaScript()) - case bsontype.Symbol: - err = vw.WriteSymbol(val.Symbol()) - case bsontype.CodeWithScope: - code, scope := val.CodeWithScope() - - var cwsw bsonrw.DocumentWriter - cwsw, err = vw.WriteCodeWithScope(code) - if err != nil { - break - } - - err = pc.encodeDocument(ec, cwsw, scope) - case bsontype.Int32: - err = vw.WriteInt32(val.Int32()) - case bsontype.Timestamp: - err = vw.WriteTimestamp(val.Timestamp()) - case bsontype.Int64: - err = vw.WriteInt64(val.Int64()) - case bsontype.Decimal128: - err = vw.WriteDecimal128(val.Decimal128()) - case bsontype.MinKey: - err = vw.WriteMinKey() - case bsontype.MaxKey: - err = vw.WriteMaxKey() - default: - err = fmt.Errorf("%T is not a valid BSON type to encode", val.Type()) - } - - return err -} - -func (pc PrimitiveCodecs) valueDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val *Val) error { - switch vr.Type() { - case bsontype.Double: - f64, err := vr.ReadDouble() - if err != nil { - return err - } - *val = Double(f64) - case bsontype.String: - str, err := vr.ReadString() - if err != nil { - return err - } - *val = String(str) - case bsontype.EmbeddedDocument: - var embeddedDoc Doc - err := pc.documentDecodeValue(dc, vr, &embeddedDoc) - if err != nil { - return err - } - *val = Document(embeddedDoc) - case bsontype.Array: - arr := reflect.New(tArray).Elem() - err := pc.ArrayDecodeValue(dc, vr, arr) - if err != nil { - return err - } - *val = Array(arr.Interface().(Arr)) - case bsontype.Binary: - data, subtype, err := vr.ReadBinary() - if err != nil { - return err - } - *val = Binary(subtype, data) - case bsontype.Undefined: - err := vr.ReadUndefined() - if err != nil { - return err - } - *val = Undefined() - case bsontype.ObjectID: - oid, err := vr.ReadObjectID() - if err != nil { - return err - } - *val = ObjectID(oid) - case bsontype.Boolean: - b, err := vr.ReadBoolean() - if err != nil { - return err - } - *val = Boolean(b) - case bsontype.DateTime: - dt, err := vr.ReadDateTime() - if err != nil { - return err - } - *val = DateTime(dt) - case bsontype.Null: - err := vr.ReadNull() - if err != nil { - return err - } - *val = Null() - case bsontype.Regex: - pattern, options, err := vr.ReadRegex() - if err != nil { - return err - } - *val = Regex(pattern, options) - case bsontype.DBPointer: - ns, pointer, err := vr.ReadDBPointer() - if err != nil { - return err - } - *val = DBPointer(ns, pointer) - case bsontype.JavaScript: - js, err := vr.ReadJavascript() - if err != nil { - return err - } - *val = JavaScript(js) - case bsontype.Symbol: - symbol, err := vr.ReadSymbol() - if err != nil { - return err - } - *val = Symbol(symbol) - case bsontype.CodeWithScope: - code, scope, err := vr.ReadCodeWithScope() - if err != nil { - return err - } - var scopeDoc Doc - err = pc.decodeDocument(dc, scope, &scopeDoc) - if err != nil { - return err - } - *val = CodeWithScope(code, scopeDoc) - case bsontype.Int32: - i32, err := vr.ReadInt32() - if err != nil { - return err - } - *val = Int32(i32) - case bsontype.Timestamp: - t, i, err := vr.ReadTimestamp() - if err != nil { - return err - } - *val = Timestamp(t, i) - case bsontype.Int64: - i64, err := vr.ReadInt64() - if err != nil { - return err - } - *val = Int64(i64) - case bsontype.Decimal128: - d128, err := vr.ReadDecimal128() - if err != nil { - return err - } - *val = Decimal128(d128) - case bsontype.MinKey: - err := vr.ReadMinKey() - if err != nil { - return err - } - *val = MinKey() - case bsontype.MaxKey: - err := vr.ReadMaxKey() - if err != nil { - return err - } - *val = MaxKey() - default: - return fmt.Errorf("Cannot read unknown BSON type %s", vr.Type()) - } - - return nil -} diff --git a/x/bsonx/primitive_codecs_test.go b/x/bsonx/primitive_codecs_test.go deleted file mode 100644 index 744ea8bcaf..0000000000 --- a/x/bsonx/primitive_codecs_test.go +++ /dev/null @@ -1,910 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2022-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "errors" - "fmt" - "reflect" - "testing" - - "github.com/google/go-cmp/cmp" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/bsoncodec" - "go.mongodb.org/mongo-driver/bson/bsonrw" - "go.mongodb.org/mongo-driver/bson/bsonrw/bsonrwtest" - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -func TestDefaultValueEncoders(t *testing.T) { - var pcx PrimitiveCodecs - - var wrong = func(string, string) string { return "wrong" } - - type subtest struct { - name string - val interface{} - ectx *bsoncodec.EncodeContext - llvrw *bsonrwtest.ValueReaderWriter - invoke bsonrwtest.Invoked - err error - } - - testCases := []struct { - name string - ve bsoncodec.ValueEncoder - subtests []subtest - }{ - { - "ValueEncodeValue", - bsoncodec.ValueEncoderFunc(pcx.ValueEncodeValue), - []subtest{ - { - "wrong type", - wrong, - nil, - nil, - bsonrwtest.Nothing, - bsoncodec.ValueEncoderError{Name: "ValueEncodeValue", Types: []reflect.Type{tValue}, Received: reflect.ValueOf(wrong)}, - }, - {"empty value", Val{}, nil, nil, bsonrwtest.WriteNull, nil}, - { - "success", - Null(), - &bsoncodec.EncodeContext{Registry: DefaultRegistry}, - &bsonrwtest.ValueReaderWriter{}, - bsonrwtest.WriteNull, - nil, - }, - }, - }, - { - "ElementSliceEncodeValue", - bsoncodec.ValueEncoderFunc(pcx.ElementSliceEncodeValue), - []subtest{ - { - "wrong type", - wrong, - nil, - nil, - bsonrwtest.Nothing, - bsoncodec.ValueEncoderError{ - Name: "ElementSliceEncodeValue", - Types: []reflect.Type{tElementSlice}, - Received: reflect.ValueOf(wrong), - }, - }, - }, - }, - { - "ArrayEncodeValue", - bsoncodec.ValueEncoderFunc(pcx.ArrayEncodeValue), - []subtest{ - { - "wrong type", - wrong, - nil, - nil, - bsonrwtest.Nothing, - bsoncodec.ValueEncoderError{Name: "ArrayEncodeValue", Types: []reflect.Type{tArray}, Received: reflect.ValueOf(wrong)}, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - for _, subtest := range tc.subtests { - t.Run(subtest.name, func(t *testing.T) { - var ec bsoncodec.EncodeContext - if subtest.ectx != nil { - ec = *subtest.ectx - } - llvrw := new(bsonrwtest.ValueReaderWriter) - if subtest.llvrw != nil { - llvrw = subtest.llvrw - } - llvrw.T = t - err := tc.ve.EncodeValue(ec, llvrw, reflect.ValueOf(subtest.val)) - if !compareErrors(err, subtest.err) { - t.Errorf("Errors do not match. got %v; want %v", err, subtest.err) - } - invoked := llvrw.Invoked - if !cmp.Equal(invoked, subtest.invoke) { - t.Errorf("Incorrect method invoked. got %v; want %v", invoked, subtest.invoke) - } - }) - } - }) - } - - t.Run("DocumentEncodeValue", func(t *testing.T) { - t.Run("ValueEncoderError", func(t *testing.T) { - val := reflect.ValueOf(bool(true)) - want := bsoncodec.ValueEncoderError{Name: "DocumentEncodeValue", Types: []reflect.Type{tDocument}, Received: val} - got := (PrimitiveCodecs{}).DocumentEncodeValue(bsoncodec.EncodeContext{}, nil, val) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("WriteDocument Error", func(t *testing.T) { - want := errors.New("WriteDocument Error") - llvrw := &bsonrwtest.ValueReaderWriter{ - T: t, - Err: want, - ErrAfter: bsonrwtest.WriteDocument, - } - got := (PrimitiveCodecs{}).DocumentEncodeValue(bsoncodec.EncodeContext{}, llvrw, reflect.MakeSlice(tDocument, 0, 0)) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("encodeDocument errors", func(t *testing.T) { - ec := bsoncodec.EncodeContext{} - err := errors.New("encodeDocument error") - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - testCases := []struct { - name string - ec bsoncodec.EncodeContext - llvrw *bsonrwtest.ValueReaderWriter - doc Doc - err error - }{ - { - "WriteDocumentElement", - ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("wde error"), ErrAfter: bsonrwtest.WriteDocumentElement}, - Doc{{"foo", Null()}}, - errors.New("wde error"), - }, - { - "WriteDouble", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDouble}, - Doc{{"foo", Double(3.14159)}}, err, - }, - { - "WriteString", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteString}, - Doc{{"foo", String("bar")}}, err, - }, - { - "WriteDocument (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t}, - Doc{{"foo", Document(Doc{{"bar", Null()}})}}, - bsoncodec.ErrNoEncoder{Type: tDocument}, - }, - { - "WriteArray (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t}, - Doc{{"foo", Array(Arr{Null()})}}, - bsoncodec.ErrNoEncoder{Type: tArray}, - }, - { - "WriteBinary", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBinaryWithSubtype}, - Doc{{"foo", Binary(0xFF, []byte{0x01, 0x02, 0x03})}}, err, - }, - { - "WriteUndefined", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteUndefined}, - Doc{{"foo", Undefined()}}, err, - }, - { - "WriteObjectID", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteObjectID}, - Doc{{"foo", ObjectID(oid)}}, err, - }, - { - "WriteBoolean", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBoolean}, - Doc{{"foo", Boolean(true)}}, err, - }, - { - "WriteDateTime", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDateTime}, - Doc{{"foo", DateTime(1234567890)}}, err, - }, - { - "WriteNull", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteNull}, - Doc{{"foo", Null()}}, err, - }, - { - "WriteRegex", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteRegex}, - Doc{{"foo", Regex("bar", "baz")}}, err, - }, - { - "WriteDBPointer", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDBPointer}, - Doc{{"foo", DBPointer("bar", oid)}}, err, - }, - { - "WriteJavascript", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteJavascript}, - Doc{{"foo", JavaScript("var hello = 'world';")}}, err, - }, - { - "WriteSymbol", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteSymbol}, - Doc{{"foo", Symbol("symbolbaz")}}, err, - }, - { - "WriteCodeWithScope (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteCodeWithScope}, - Doc{{"foo", CodeWithScope("var hello = 'world';", Doc{}.Append("bar", Null()))}}, - err, - }, - { - "WriteInt32", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt32}, - Doc{{"foo", Int32(12345)}}, err, - }, - { - "WriteInt64", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt64}, - Doc{{"foo", Int64(1234567890)}}, err, - }, - { - "WriteTimestamp", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteTimestamp}, - Doc{{"foo", Timestamp(10, 20)}}, err, - }, - { - "WriteDecimal128", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDecimal128}, - Doc{{"foo", Decimal128(primitive.NewDecimal128(10, 20))}}, err, - }, - { - "WriteMinKey", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMinKey}, - Doc{{"foo", MinKey()}}, err, - }, - { - "WriteMaxKey", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMaxKey}, - Doc{{"foo", MaxKey()}}, err, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := (PrimitiveCodecs{}).DocumentEncodeValue(tc.ec, tc.llvrw, reflect.ValueOf(tc.doc)) - if !compareErrors(err, tc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, tc.err) - } - }) - } - }) - - t.Run("success", func(t *testing.T) { - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - d128 := primitive.NewDecimal128(10, 20) - want := Doc{ - {"a", Double(3.14159)}, {"b", String("foo")}, - {"c", Document(Doc{{"aa", Null()}})}, {"d", Array(Arr{Null()})}, - {"e", Binary(0xFF, []byte{0x01, 0x02, 0x03})}, {"f", Undefined()}, - {"g", ObjectID(oid)}, {"h", Boolean(true)}, - {"i", DateTime(1234567890)}, {"j", Null()}, - {"k", Regex("foo", "abr")}, - {"l", DBPointer("foobar", oid)}, {"m", JavaScript("var hello = 'world';")}, - {"n", Symbol("bazqux")}, - {"o", CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}})}, - {"p", Int32(12345)}, - {"q", Timestamp(10, 20)}, {"r", Int64(1234567890)}, {"s", Decimal128(d128)}, {"t", MinKey()}, {"u", MaxKey()}, - } - slc := make(bsonrw.SliceWriter, 0, 128) - vw, err := bsonrw.NewBSONValueWriter(&slc) - noerr(t, err) - - ec := bsoncodec.EncodeContext{Registry: DefaultRegistry} - err = (PrimitiveCodecs{}).DocumentEncodeValue(ec, vw, reflect.ValueOf(want)) - noerr(t, err) - got, err := ReadDoc(slc) - noerr(t, err) - if !got.Equal(want) { - t.Error("Documents do not match") - t.Errorf("\ngot :%v\nwant:%v", got, want) - } - }) - }) - - t.Run("ArrayEncodeValue", func(t *testing.T) { - t.Run("CodecEncodeError", func(t *testing.T) { - val := reflect.ValueOf(bool(true)) - want := bsoncodec.ValueEncoderError{Name: "ArrayEncodeValue", Types: []reflect.Type{tArray}, Received: val} - got := (PrimitiveCodecs{}).ArrayEncodeValue(bsoncodec.EncodeContext{}, nil, val) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("WriteArray Error", func(t *testing.T) { - want := errors.New("WriteArray Error") - llvrw := &bsonrwtest.ValueReaderWriter{ - T: t, - Err: want, - ErrAfter: bsonrwtest.WriteArray, - } - got := (PrimitiveCodecs{}).ArrayEncodeValue(bsoncodec.EncodeContext{}, llvrw, reflect.MakeSlice(tArray, 0, 0)) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("encode array errors", func(t *testing.T) { - ec := bsoncodec.EncodeContext{} - err := errors.New("encode array error") - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - testCases := []struct { - name string - ec bsoncodec.EncodeContext - llvrw *bsonrwtest.ValueReaderWriter - arr Arr - err error - }{ - { - "WriteDocumentElement", - ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("wde error"), ErrAfter: bsonrwtest.WriteArrayElement}, - Arr{Null()}, - errors.New("wde error"), - }, - { - "WriteDouble", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDouble}, - Arr{Double(3.14159)}, err, - }, - { - "WriteString", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteString}, - Arr{String("bar")}, err, - }, - { - "WriteDocument (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t}, - Arr{Document(Doc{{"bar", Null()}})}, - bsoncodec.ErrNoEncoder{Type: tDocument}, - }, - { - "WriteArray (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t}, - Arr{Array(Arr{Null()})}, - bsoncodec.ErrNoEncoder{Type: tArray}, - }, - { - "WriteBinary", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBinaryWithSubtype}, - Arr{Binary(0xFF, []byte{0x01, 0x02, 0x03})}, err, - }, - { - "WriteUndefined", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteUndefined}, - Arr{Undefined()}, err, - }, - { - "WriteObjectID", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteObjectID}, - Arr{ObjectID(oid)}, err, - }, - { - "WriteBoolean", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteBoolean}, - Arr{Boolean(true)}, err, - }, - { - "WriteDateTime", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDateTime}, - Arr{DateTime(1234567890)}, err, - }, - { - "WriteNull", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteNull}, - Arr{Null()}, err, - }, - { - "WriteRegex", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteRegex}, - Arr{Regex("bar", "baz")}, err, - }, - { - "WriteDBPointer", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDBPointer}, - Arr{DBPointer("bar", oid)}, err, - }, - { - "WriteJavascript", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteJavascript}, - Arr{JavaScript("var hello = 'world';")}, err, - }, - { - "WriteSymbol", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteSymbol}, - Arr{Symbol("symbolbaz")}, err, - }, - { - "WriteCodeWithScope (Lookup)", bsoncodec.EncodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteCodeWithScope}, - Arr{CodeWithScope("var hello = 'world';", Doc{{"bar", Null()}})}, - err, - }, - { - "WriteInt32", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt32}, - Arr{Int32(12345)}, err, - }, - { - "WriteInt64", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteInt64}, - Arr{Int64(1234567890)}, err, - }, - { - "WriteTimestamp", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteTimestamp}, - Arr{Timestamp(10, 20)}, err, - }, - { - "WriteDecimal128", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteDecimal128}, - Arr{Decimal128(primitive.NewDecimal128(10, 20))}, err, - }, - { - "WriteMinKey", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMinKey}, - Arr{MinKey()}, err, - }, - { - "WriteMaxKey", ec, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.WriteMaxKey}, - Arr{MaxKey()}, err, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := (PrimitiveCodecs{}).ArrayEncodeValue(tc.ec, tc.llvrw, reflect.ValueOf(tc.arr)) - if !compareErrors(err, tc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, tc.err) - } - }) - } - }) - - t.Run("success", func(t *testing.T) { - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - d128 := primitive.NewDecimal128(10, 20) - want := Arr{ - Double(3.14159), String("foo"), Document(Doc{{"aa", Null()}}), - Array(Arr{Null()}), - Binary(0xFF, []byte{0x01, 0x02, 0x03}), Undefined(), - ObjectID(oid), Boolean(true), DateTime(1234567890), Null(), Regex("foo", "abr"), - DBPointer("foobar", oid), JavaScript("var hello = 'world';"), Symbol("bazqux"), - CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}}), Int32(12345), - Timestamp(10, 20), Int64(1234567890), Decimal128(d128), MinKey(), MaxKey(), - } - - ec := bsoncodec.EncodeContext{Registry: DefaultRegistry} - - slc := make(bsonrw.SliceWriter, 0, 128) - vw, err := bsonrw.NewBSONValueWriter(&slc) - noerr(t, err) - - dr, err := vw.WriteDocument() - noerr(t, err) - vr, err := dr.WriteDocumentElement("foo") - noerr(t, err) - - err = (PrimitiveCodecs{}).ArrayEncodeValue(ec, vr, reflect.ValueOf(want)) - noerr(t, err) - - err = dr.WriteDocumentEnd() - noerr(t, err) - - val, err := bsoncore.Document(slc).LookupErr("foo") - noerr(t, err) - rgot := val.Array() - doc, err := ReadDoc(rgot) - noerr(t, err) - got := make(Arr, 0) - for _, elem := range doc { - got = append(got, elem.Value) - } - if !got.Equal(want) { - t.Error("Documents do not match") - t.Errorf("\ngot :%v\nwant:%v", got, want) - } - }) - }) -} - -func TestDefaultValueDecoders(t *testing.T) { - var pcx PrimitiveCodecs - - var wrong = func(string, string) string { return "wrong" } - - const cansetreflectiontest = "cansetreflectiontest" - - type subtest struct { - name string - val interface{} - dctx *bsoncodec.DecodeContext - llvrw *bsonrwtest.ValueReaderWriter - invoke bsonrwtest.Invoked - err error - } - - testCases := []struct { - name string - vd bsoncodec.ValueDecoder - subtests []subtest - }{ - { - "ValueDecodeValue", - bsoncodec.ValueDecoderFunc(pcx.ValueDecodeValue), - []subtest{ - { - "wrong type", - wrong, - nil, - nil, - bsonrwtest.Nothing, - bsoncodec.ValueDecoderError{ - Name: "ValueDecodeValue", - Types: []reflect.Type{tValue}, - Received: reflect.ValueOf(wrong), - }, - }, - { - "invalid value", - (*Val)(nil), - nil, - nil, - bsonrwtest.Nothing, - bsoncodec.ValueDecoderError{ - Name: "ValueDecodeValue", - Types: []reflect.Type{tValue}, - Received: reflect.ValueOf((*Val)(nil)), - }, - }, - { - "success", - Double(3.14159), - &bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14159)}, - bsonrwtest.ReadDouble, - nil, - }, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - for _, rc := range tc.subtests { - t.Run(rc.name, func(t *testing.T) { - var dc bsoncodec.DecodeContext - if rc.dctx != nil { - dc = *rc.dctx - } - llvrw := new(bsonrwtest.ValueReaderWriter) - if rc.llvrw != nil { - llvrw = rc.llvrw - } - llvrw.T = t - // var got interface{} - if rc.val == cansetreflectiontest { // We're doing a CanSet reflection test - err := tc.vd.DecodeValue(dc, llvrw, reflect.Value{}) - if !compareErrors(err, rc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, rc.err) - } - - val := reflect.New(reflect.TypeOf(rc.val)).Elem() - err = tc.vd.DecodeValue(dc, llvrw, val) - if !compareErrors(err, rc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, rc.err) - } - return - } - var val reflect.Value - if rtype := reflect.TypeOf(rc.val); rtype != nil { - val = reflect.New(rtype).Elem() - } - want := rc.val - defer func() { - if err := recover(); err != nil { - fmt.Println(t.Name()) - panic(err) - } - }() - err := tc.vd.DecodeValue(dc, llvrw, val) - if !compareErrors(err, rc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, rc.err) - } - invoked := llvrw.Invoked - if !cmp.Equal(invoked, rc.invoke) { - t.Errorf("Incorrect method invoked. got %v; want %v", invoked, rc.invoke) - } - var got interface{} - if val.IsValid() && val.CanInterface() { - got = val.Interface() - } - if rc.err == nil && !cmp.Equal(got, want, cmp.Comparer(compareValues)) { - t.Errorf("Values do not match. got (%T)%v; want (%T)%v", got, got, want, want) - } - }) - } - }) - } - - t.Run("DocumentDecodeValue", func(t *testing.T) { - t.Run("CodecDecodeError", func(t *testing.T) { - val := reflect.New(reflect.TypeOf(false)).Elem() - want := bsoncodec.ValueDecoderError{Name: "DocumentDecodeValue", Types: []reflect.Type{tDocument}, Received: val} - got := pcx.DocumentDecodeValue(bsoncodec.DecodeContext{}, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.EmbeddedDocument}, val) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("ReadDocument Error", func(t *testing.T) { - want := errors.New("ReadDocument Error") - llvrw := &bsonrwtest.ValueReaderWriter{ - T: t, - Err: want, - ErrAfter: bsonrwtest.ReadDocument, - BSONType: bsontype.EmbeddedDocument, - } - got := pcx.DocumentDecodeValue(bsoncodec.DecodeContext{}, llvrw, reflect.New(reflect.TypeOf(Doc{})).Elem()) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("decodeDocument errors", func(t *testing.T) { - dc := bsoncodec.DecodeContext{} - err := errors.New("decodeDocument error") - testCases := []struct { - name string - dc bsoncodec.DecodeContext - llvrw *bsonrwtest.ValueReaderWriter - err error - }{ - { - "ReadElement", - dc, - &bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("re error"), ErrAfter: bsonrwtest.ReadElement}, - errors.New("re error"), - }, - {"ReadDouble", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDouble, BSONType: bsontype.Double}, err}, - {"ReadString", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadString, BSONType: bsontype.String}, err}, - {"ReadBinary", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBinary, BSONType: bsontype.Binary}, err}, - {"ReadUndefined", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadUndefined, BSONType: bsontype.Undefined}, err}, - {"ReadObjectID", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadObjectID, BSONType: bsontype.ObjectID}, err}, - {"ReadBoolean", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBoolean, BSONType: bsontype.Boolean}, err}, - {"ReadDateTime", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDateTime, BSONType: bsontype.DateTime}, err}, - {"ReadNull", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadNull, BSONType: bsontype.Null}, err}, - {"ReadRegex", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadRegex, BSONType: bsontype.Regex}, err}, - {"ReadDBPointer", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDBPointer, BSONType: bsontype.DBPointer}, err}, - {"ReadJavascript", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadJavascript, BSONType: bsontype.JavaScript}, err}, - {"ReadSymbol", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadSymbol, BSONType: bsontype.Symbol}, err}, - { - "ReadCodeWithScope (Lookup)", bsoncodec.DecodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadCodeWithScope, BSONType: bsontype.CodeWithScope}, - err, - }, - {"ReadInt32", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt32, BSONType: bsontype.Int32}, err}, - {"ReadInt64", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt64, BSONType: bsontype.Int64}, err}, - {"ReadTimestamp", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadTimestamp, BSONType: bsontype.Timestamp}, err}, - {"ReadDecimal128", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDecimal128, BSONType: bsontype.Decimal128}, err}, - {"ReadMinKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMinKey, BSONType: bsontype.MinKey}, err}, - {"ReadMaxKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMaxKey, BSONType: bsontype.MaxKey}, err}, - {"Invalid Type", dc, &bsonrwtest.ValueReaderWriter{T: t, BSONType: bsontype.Type(0)}, fmt.Errorf("Cannot read unknown BSON type %s", bsontype.Type(0))}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := pcx.DecodeDocument(tc.dc, tc.llvrw, new(Doc)) - if !compareErrors(err, tc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, tc.err) - } - }) - } - }) - - t.Run("success", func(t *testing.T) { - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - d128 := primitive.NewDecimal128(10, 20) - want := Doc{ - {"a", Double(3.14159)}, {"b", String("foo")}, - {"c", Document(Doc{{"aa", Null()}})}, - {"d", Array(Arr{Null()})}, - {"e", Binary(0xFF, []byte{0x01, 0x02, 0x03})}, {"f", Undefined()}, - {"g", ObjectID(oid)}, {"h", Boolean(true)}, - {"i", DateTime(1234567890)}, {"j", Null()}, {"k", Regex("foo", "bar")}, - {"l", DBPointer("foobar", oid)}, {"m", JavaScript("var hello = 'world';")}, - {"n", Symbol("bazqux")}, - {"o", CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}})}, - {"p", Int32(12345)}, - {"q", Timestamp(10, 20)}, {"r", Int64(1234567890)}, - {"s", Decimal128(d128)}, {"t", MinKey()}, {"u", MaxKey()}, - } - got := reflect.New(reflect.TypeOf(Doc{})).Elem() - dc := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()} - b, err := want.MarshalBSON() - noerr(t, err) - err = pcx.DocumentDecodeValue(dc, bsonrw.NewBSONDocumentReader(b), got) - noerr(t, err) - if !got.Interface().(Doc).Equal(want) { - t.Error("Documents do not match") - t.Errorf("\ngot :%v\nwant:%v", got, want) - } - }) - }) - - t.Run("ArrayDecodeValue", func(t *testing.T) { - t.Run("CodecDecodeError", func(t *testing.T) { - val := reflect.New(reflect.TypeOf(false)).Elem() - want := bsoncodec.ValueDecoderError{Name: "ArrayDecodeValue", Types: []reflect.Type{tArray}, Received: val} - got := pcx.ArrayDecodeValue(bsoncodec.DecodeContext{}, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array}, val) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("ReadArray Error", func(t *testing.T) { - want := errors.New("ReadArray Error") - llvrw := &bsonrwtest.ValueReaderWriter{ - T: t, - Err: want, - ErrAfter: bsonrwtest.ReadArray, - BSONType: bsontype.Array, - } - got := pcx.ArrayDecodeValue(bsoncodec.DecodeContext{}, llvrw, reflect.New(tArray).Elem()) - if !compareErrors(got, want) { - t.Errorf("Errors do not match. got %v; want %v", got, want) - } - }) - t.Run("decode array errors", func(t *testing.T) { - dc := bsoncodec.DecodeContext{} - err := errors.New("decode array error") - testCases := []struct { - name string - dc bsoncodec.DecodeContext - llvrw *bsonrwtest.ValueReaderWriter - err error - }{ - { - "ReadValue", - dc, - &bsonrwtest.ValueReaderWriter{T: t, Err: errors.New("re error"), ErrAfter: bsonrwtest.ReadValue}, - errors.New("re error"), - }, - {"ReadDouble", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDouble, BSONType: bsontype.Double}, err}, - {"ReadString", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadString, BSONType: bsontype.String}, err}, - {"ReadBinary", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBinary, BSONType: bsontype.Binary}, err}, - {"ReadUndefined", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadUndefined, BSONType: bsontype.Undefined}, err}, - {"ReadObjectID", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadObjectID, BSONType: bsontype.ObjectID}, err}, - {"ReadBoolean", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadBoolean, BSONType: bsontype.Boolean}, err}, - {"ReadDateTime", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDateTime, BSONType: bsontype.DateTime}, err}, - {"ReadNull", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadNull, BSONType: bsontype.Null}, err}, - {"ReadRegex", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadRegex, BSONType: bsontype.Regex}, err}, - {"ReadDBPointer", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDBPointer, BSONType: bsontype.DBPointer}, err}, - {"ReadJavascript", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadJavascript, BSONType: bsontype.JavaScript}, err}, - {"ReadSymbol", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadSymbol, BSONType: bsontype.Symbol}, err}, - { - "ReadCodeWithScope (Lookup)", bsoncodec.DecodeContext{Registry: bsoncodec.NewRegistryBuilder().Build()}, - &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadCodeWithScope, BSONType: bsontype.CodeWithScope}, - err, - }, - {"ReadInt32", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt32, BSONType: bsontype.Int32}, err}, - {"ReadInt64", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadInt64, BSONType: bsontype.Int64}, err}, - {"ReadTimestamp", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadTimestamp, BSONType: bsontype.Timestamp}, err}, - {"ReadDecimal128", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadDecimal128, BSONType: bsontype.Decimal128}, err}, - {"ReadMinKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMinKey, BSONType: bsontype.MinKey}, err}, - {"ReadMaxKey", dc, &bsonrwtest.ValueReaderWriter{T: t, Err: err, ErrAfter: bsonrwtest.ReadMaxKey, BSONType: bsontype.MaxKey}, err}, - {"Invalid Type", dc, &bsonrwtest.ValueReaderWriter{T: t, BSONType: bsontype.Type(0)}, fmt.Errorf("Cannot read unknown BSON type %s", bsontype.Type(0))}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := pcx.ArrayDecodeValue(tc.dc, tc.llvrw, reflect.New(tArray).Elem()) - if !compareErrors(err, tc.err) { - t.Errorf("Errors do not match. got %v; want %v", err, tc.err) - } - }) - } - }) - - t.Run("success", func(t *testing.T) { - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - d128 := primitive.NewDecimal128(10, 20) - want := Arr{ - Double(3.14159), String("foo"), Document(Doc{{"aa", Null()}}), - Array(Arr{Null()}), - Binary(0xFF, []byte{0x01, 0x02, 0x03}), Undefined(), - ObjectID(oid), Boolean(true), DateTime(1234567890), Null(), Regex("foo", "bar"), - DBPointer("foobar", oid), JavaScript("var hello = 'world';"), Symbol("bazqux"), - CodeWithScope("var hello = 'world';", Doc{{"ab", Null()}}), Int32(12345), - Timestamp(10, 20), Int64(1234567890), Decimal128(d128), MinKey(), MaxKey(), - } - dc := bsoncodec.DecodeContext{Registry: NewRegistryBuilder().Build()} - - b, err := Doc{{"", Array(want)}}.MarshalBSON() - noerr(t, err) - dvr := bsonrw.NewBSONDocumentReader(b) - dr, err := dvr.ReadDocument() - noerr(t, err) - _, vr, err := dr.ReadElement() - noerr(t, err) - - val := reflect.New(tArray).Elem() - err = pcx.ArrayDecodeValue(dc, vr, val) - noerr(t, err) - got := val.Interface().(Arr) - if !got.Equal(want) { - t.Error("Documents do not match") - t.Errorf("\ngot :%v\nwant:%v", got, want) - } - }) - }) - - t.Run("success path", func(t *testing.T) { - testCases := []struct { - name string - value interface{} - b []byte - err error - }{ - { - "map[string][]Element", - map[string][]Elem{"Z": {{"A", Int32(1)}, {"B", Int32(2)}, {"EC", Int32(3)}}}, - docToBytes(Doc{{"Z", Document(Doc{{"A", Int32(1)}, {"B", Int32(2)}, {"EC", Int32(3)}})}}), - nil, - }, - { - "map[string][]Value", - map[string][]Val{"Z": {Int32(1), Int32(2), Int32(3)}}, - docToBytes(Doc{{"Z", Array(Arr{Int32(1), Int32(2), Int32(3)})}}), - nil, - }, - { - "map[string]*Document", - map[string]Doc{"Z": {{"foo", Null()}}}, - docToBytes(Doc{{"Z", Document(Doc{{"foo", Null()}})}}), - nil, - }, - } - - t.Run("Decode", func(t *testing.T) { - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - vr := bsonrw.NewBSONDocumentReader(tc.b) - dec, err := bson.NewDecoderWithContext(bsoncodec.DecodeContext{Registry: DefaultRegistry}, vr) - noerr(t, err) - gotVal := reflect.New(reflect.TypeOf(tc.value)) - err = dec.Decode(gotVal.Interface()) - noerr(t, err) - got := gotVal.Elem().Interface() - want := tc.value - if diff := cmp.Diff( - got, want, - ); diff != "" { - t.Errorf("difference:\n%s", diff) - t.Errorf("Values are not equal.\ngot: %#v\nwant:%#v", got, want) - } - }) - } - }) - }) -} - -func compareValues(v1, v2 Val) bool { return v1.Equal(v2) } - -func docToBytes(d Doc) []byte { - b, err := d.MarshalBSON() - if err != nil { - panic(err) - } - return b -} diff --git a/x/bsonx/reflectionfree_d_codec.go b/x/bsonx/reflectionfree_d_codec.go deleted file mode 100644 index 7e68e55c1e..0000000000 --- a/x/bsonx/reflectionfree_d_codec.go +++ /dev/null @@ -1,1025 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "fmt" - "math" - "reflect" - "time" - - "go.mongodb.org/mongo-driver/bson/bsoncodec" - "go.mongodb.org/mongo-driver/bson/bsonrw" - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -var ( - tPrimitiveD = reflect.TypeOf(primitive.D{}) - tPrimitiveCWS = reflect.TypeOf(primitive.CodeWithScope{}) - defaultValueEncoders = bsoncodec.DefaultValueEncoders{} - defaultValueDecoders = bsoncodec.DefaultValueDecoders{} -) - -type reflectionFreeDCodec struct{} - -// ReflectionFreeDCodec is a ValueEncoder for the primitive.D type that does not use reflection. -var ReflectionFreeDCodec bsoncodec.ValueCodec = &reflectionFreeDCodec{} - -func (r *reflectionFreeDCodec) EncodeValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { - if !val.IsValid() || val.Type() != tPrimitiveD { - return bsoncodec.ValueEncoderError{Name: "DEncodeValue", Types: []reflect.Type{tPrimitiveD}, Received: val} - } - - if val.IsNil() { - return vw.WriteNull() - } - - doc := val.Interface().(primitive.D) - return r.encodeDocument(ec, vw, doc) -} - -func (r *reflectionFreeDCodec) DecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { - if !val.IsValid() || !val.CanSet() || val.Type() != tPrimitiveD { - return bsoncodec.ValueDecoderError{Name: "DDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val} - } - - switch vrType := vr.Type(); vrType { - case bsontype.Type(0), bsontype.EmbeddedDocument: - case bsontype.Null: - val.Set(reflect.Zero(val.Type())) - return vr.ReadNull() - default: - return fmt.Errorf("cannot decode %v into a primitive.D", vrType) - } - - doc, err := r.decodeDocument(dc, vr) - if err != nil { - return err - } - - val.Set(reflect.ValueOf(doc)) - return nil -} - -func (r *reflectionFreeDCodec) decodeDocument(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader) (primitive.D, error) { - dr, err := vr.ReadDocument() - if err != nil { - return nil, err - } - - doc := primitive.D{} - for { - key, elemVr, err := dr.ReadElement() - if err == bsonrw.ErrEOD { - break - } - if err != nil { - return nil, err - } - - val, err := r.decodeValue(dc, elemVr) - if err != nil { - return nil, err - } - doc = append(doc, primitive.E{Key: key, Value: val}) - } - - return doc, nil -} - -func (r *reflectionFreeDCodec) decodeArray(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader) (primitive.A, error) { - ar, err := vr.ReadArray() - if err != nil { - return nil, err - } - - array := primitive.A{} - for { - arrayValReader, err := ar.ReadValue() - if err == bsonrw.ErrEOA { - break - } - if err != nil { - return nil, err - } - - val, err := r.decodeValue(dc, arrayValReader) - if err != nil { - return nil, err - } - array = append(array, val) - } - - return array, nil -} - -func (r *reflectionFreeDCodec) decodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader) (interface{}, error) { - switch vrType := vr.Type(); vrType { - case bsontype.Null: - return nil, vr.ReadNull() - case bsontype.Double: - return vr.ReadDouble() - case bsontype.String: - return vr.ReadString() - case bsontype.Binary: - data, subtype, err := vr.ReadBinary() - if err != nil { - return nil, err - } - - return primitive.Binary{ - Data: data, - Subtype: subtype, - }, nil - case bsontype.Undefined: - return primitive.Undefined{}, vr.ReadUndefined() - case bsontype.ObjectID: - return vr.ReadObjectID() - case bsontype.Boolean: - return vr.ReadBoolean() - case bsontype.DateTime: - dt, err := vr.ReadDateTime() - if err != nil { - return nil, err - } - - return primitive.DateTime(dt), nil - case bsontype.Regex: - pattern, options, err := vr.ReadRegex() - if err != nil { - return nil, err - } - - return primitive.Regex{ - Pattern: pattern, - Options: options, - }, nil - case bsontype.DBPointer: - ns, oid, err := vr.ReadDBPointer() - if err != nil { - return nil, err - } - - return primitive.DBPointer{ - DB: ns, - Pointer: oid, - }, nil - case bsontype.JavaScript: - js, err := vr.ReadJavascript() - if err != nil { - return nil, err - } - - return primitive.JavaScript(js), nil - case bsontype.Symbol: - sym, err := vr.ReadSymbol() - if err != nil { - return nil, err - } - - return primitive.Symbol(sym), nil - case bsontype.CodeWithScope: - cws := reflect.New(tPrimitiveCWS).Elem() - err := defaultValueDecoders.CodeWithScopeDecodeValue(dc, vr, cws) - if err != nil { - return nil, err - } - - return cws.Interface().(primitive.CodeWithScope), nil - case bsontype.Int32: - return vr.ReadInt32() - case bsontype.Int64: - return vr.ReadInt64() - case bsontype.Timestamp: - t, i, err := vr.ReadTimestamp() - if err != nil { - return nil, err - } - - return primitive.Timestamp{ - T: t, - I: i, - }, nil - case bsontype.Decimal128: - return vr.ReadDecimal128() - case bsontype.MinKey: - return primitive.MinKey{}, vr.ReadMinKey() - case bsontype.MaxKey: - return primitive.MaxKey{}, vr.ReadMaxKey() - case bsontype.Type(0), bsontype.EmbeddedDocument: - return r.decodeDocument(dc, vr) - case bsontype.Array: - return r.decodeArray(dc, vr) - default: - return nil, fmt.Errorf("cannot decode invalid BSON type %s", vrType) - } -} - -func (r *reflectionFreeDCodec) encodeDocumentValue(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, v interface{}) error { - switch val := v.(type) { - case int: - return r.encodeInt(vw, val) - case int8: - return vw.WriteInt32(int32(val)) - case int16: - return vw.WriteInt32(int32(val)) - case int32: - return vw.WriteInt32(val) - case int64: - return r.encodeInt64(ec, vw, val) - case uint: - return r.encodeUint64(ec, vw, uint64(val)) - case uint8: - return vw.WriteInt32(int32(val)) - case uint16: - return vw.WriteInt32(int32(val)) - case uint32: - return r.encodeUint64(ec, vw, uint64(val)) - case uint64: - return r.encodeUint64(ec, vw, val) - case float32: - return vw.WriteDouble(float64(val)) - case float64: - return vw.WriteDouble(val) - case []byte: - return vw.WriteBinary(val) - case primitive.Binary: - return vw.WriteBinaryWithSubtype(val.Data, val.Subtype) - case bool: - return vw.WriteBoolean(val) - case primitive.CodeWithScope: - return defaultValueEncoders.CodeWithScopeEncodeValue(ec, vw, reflect.ValueOf(val)) - case primitive.DBPointer: - return vw.WriteDBPointer(val.DB, val.Pointer) - case primitive.DateTime: - return vw.WriteDateTime(int64(val)) - case time.Time: - dt := primitive.NewDateTimeFromTime(val) - return vw.WriteDateTime(int64(dt)) - case primitive.Decimal128: - return vw.WriteDecimal128(val) - case primitive.JavaScript: - return vw.WriteJavascript(string(val)) - case primitive.MinKey: - return vw.WriteMinKey() - case primitive.MaxKey: - return vw.WriteMaxKey() - case primitive.Null, nil: - return vw.WriteNull() - case primitive.ObjectID: - return vw.WriteObjectID(val) - case primitive.Regex: - return vw.WriteRegex(val.Pattern, val.Options) - case string: - return vw.WriteString(val) - case primitive.Symbol: - return vw.WriteSymbol(string(val)) - case primitive.Timestamp: - return vw.WriteTimestamp(val.T, val.I) - case primitive.Undefined: - return vw.WriteUndefined() - case primitive.D: - return r.encodeDocument(ec, vw, val) - case primitive.A: - return r.encodePrimitiveA(ec, vw, val) - case []interface{}: - return r.encodePrimitiveA(ec, vw, val) - case []primitive.D: - return r.encodeSliceD(ec, vw, val) - case []int: - return r.encodeSliceInt(vw, val) - case []int8: - return r.encodeSliceInt8(vw, val) - case []int16: - return r.encodeSliceInt16(vw, val) - case []int32: - return r.encodeSliceInt32(vw, val) - case []int64: - return r.encodeSliceInt64(ec, vw, val) - case []uint: - return r.encodeSliceUint(ec, vw, val) - case []uint16: - return r.encodeSliceUint16(vw, val) - case []uint32: - return r.encodeSliceUint32(ec, vw, val) - case []uint64: - return r.encodeSliceUint64(ec, vw, val) - case [][]byte: - return r.encodeSliceByteSlice(vw, val) - case []primitive.Binary: - return r.encodeSliceBinary(vw, val) - case []bool: - return r.encodeSliceBoolean(vw, val) - case []primitive.CodeWithScope: - return r.encodeSliceCWS(ec, vw, val) - case []primitive.DBPointer: - return r.encodeSliceDBPointer(vw, val) - case []primitive.DateTime: - return r.encodeSliceDateTime(vw, val) - case []time.Time: - return r.encodeSliceTimeTime(vw, val) - case []primitive.Decimal128: - return r.encodeSliceDecimal128(vw, val) - case []float32: - return r.encodeSliceFloat32(vw, val) - case []float64: - return r.encodeSliceFloat64(vw, val) - case []primitive.JavaScript: - return r.encodeSliceJavaScript(vw, val) - case []primitive.MinKey: - return r.encodeSliceMinKey(vw, val) - case []primitive.MaxKey: - return r.encodeSliceMaxKey(vw, val) - case []primitive.Null: - return r.encodeSliceNull(vw, val) - case []primitive.ObjectID: - return r.encodeSliceObjectID(vw, val) - case []primitive.Regex: - return r.encodeSliceRegex(vw, val) - case []string: - return r.encodeSliceString(vw, val) - case []primitive.Symbol: - return r.encodeSliceSymbol(vw, val) - case []primitive.Timestamp: - return r.encodeSliceTimestamp(vw, val) - case []primitive.Undefined: - return r.encodeSliceUndefined(vw, val) - default: - return fmt.Errorf("value of type %T not supported", v) - } -} - -func (r *reflectionFreeDCodec) encodeInt(vw bsonrw.ValueWriter, val int) error { - if fitsIn32Bits(int64(val)) { - return vw.WriteInt32(int32(val)) - } - return vw.WriteInt64(int64(val)) -} - -func (r *reflectionFreeDCodec) encodeInt64(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val int64) error { - if ec.MinSize && fitsIn32Bits(val) { - return vw.WriteInt32(int32(val)) - } - return vw.WriteInt64(val) -} - -func (r *reflectionFreeDCodec) encodeUint64(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val uint64) error { - if ec.MinSize && val <= math.MaxInt32 { - return vw.WriteInt32(int32(val)) - } - if val > math.MaxInt64 { - return fmt.Errorf("%d overflows int64", val) - } - - return vw.WriteInt64(int64(val)) -} - -func (r *reflectionFreeDCodec) encodeDocument(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, doc primitive.D) error { - dw, err := vw.WriteDocument() - if err != nil { - return err - } - - for _, elem := range doc { - docValWriter, err := dw.WriteDocumentElement(elem.Key) - if err != nil { - return err - } - - if err := r.encodeDocumentValue(ec, docValWriter, elem.Value); err != nil { - return err - } - } - - return dw.WriteDocumentEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceByteSlice(vw bsonrw.ValueWriter, arr [][]byte) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteBinary(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceBinary(vw bsonrw.ValueWriter, arr []primitive.Binary) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteBinaryWithSubtype(val.Data, val.Subtype); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceBoolean(vw bsonrw.ValueWriter, arr []bool) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteBoolean(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceCWS(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.CodeWithScope) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := defaultValueEncoders.CodeWithScopeEncodeValue(ec, arrayValWriter, reflect.ValueOf(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceDBPointer(vw bsonrw.ValueWriter, arr []primitive.DBPointer) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteDBPointer(val.DB, val.Pointer); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceDateTime(vw bsonrw.ValueWriter, arr []primitive.DateTime) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteDateTime(int64(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceTimeTime(vw bsonrw.ValueWriter, arr []time.Time) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - dt := primitive.NewDateTimeFromTime(val) - if err := arrayValWriter.WriteDateTime(int64(dt)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceDecimal128(vw bsonrw.ValueWriter, arr []primitive.Decimal128) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteDecimal128(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceFloat32(vw bsonrw.ValueWriter, arr []float32) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteDouble(float64(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceFloat64(vw bsonrw.ValueWriter, arr []float64) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteDouble(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceJavaScript(vw bsonrw.ValueWriter, arr []primitive.JavaScript) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteJavascript(string(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceMinKey(vw bsonrw.ValueWriter, arr []primitive.MinKey) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteMinKey(); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceMaxKey(vw bsonrw.ValueWriter, arr []primitive.MaxKey) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteMaxKey(); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceNull(vw bsonrw.ValueWriter, arr []primitive.Null) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteNull(); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceObjectID(vw bsonrw.ValueWriter, arr []primitive.ObjectID) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteObjectID(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceRegex(vw bsonrw.ValueWriter, arr []primitive.Regex) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteRegex(val.Pattern, val.Options); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceString(vw bsonrw.ValueWriter, arr []string) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteString(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceSymbol(vw bsonrw.ValueWriter, arr []primitive.Symbol) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteSymbol(string(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceTimestamp(vw bsonrw.ValueWriter, arr []primitive.Timestamp) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteTimestamp(val.T, val.I); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceUndefined(vw bsonrw.ValueWriter, arr []primitive.Undefined) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteUndefined(); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodePrimitiveA(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr primitive.A) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeDocumentValue(ec, arrayValWriter, val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceD(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []primitive.D) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeDocument(ec, arrayValWriter, val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceInt(vw bsonrw.ValueWriter, arr []int) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeInt(arrayValWriter, val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceInt8(vw bsonrw.ValueWriter, arr []int8) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteInt32(int32(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceInt16(vw bsonrw.ValueWriter, arr []int16) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteInt32(int32(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceInt32(vw bsonrw.ValueWriter, arr []int32) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteInt32(val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceInt64(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []int64) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeInt64(ec, arrayValWriter, val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceUint(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []uint) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeUint64(ec, arrayValWriter, uint64(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceUint16(vw bsonrw.ValueWriter, arr []uint16) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := arrayValWriter.WriteInt32(int32(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceUint32(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []uint32) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeUint64(ec, arrayValWriter, uint64(val)); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func (r *reflectionFreeDCodec) encodeSliceUint64(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, arr []uint64) error { - aw, err := vw.WriteArray() - if err != nil { - return err - } - - for _, val := range arr { - arrayValWriter, err := aw.WriteArrayElement() - if err != nil { - return err - } - - if err := r.encodeUint64(ec, arrayValWriter, val); err != nil { - return err - } - } - - return aw.WriteArrayEnd() -} - -func fitsIn32Bits(i int64) bool { - return math.MinInt32 <= i && i <= math.MaxInt32 -} diff --git a/x/bsonx/reflectionfree_d_codec_test.go b/x/bsonx/reflectionfree_d_codec_test.go deleted file mode 100644 index d13bfe1abc..0000000000 --- a/x/bsonx/reflectionfree_d_codec_test.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2022-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "reflect" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/internal/testutil/assert" -) - -func TestReflectionFreeDCodec(t *testing.T) { - assert.RegisterOpts(reflect.TypeOf(primitive.D{}), cmp.AllowUnexported(primitive.Decimal128{})) - - now := time.Now() - oid := primitive.NewObjectID() - d128 := primitive.NewDecimal128(10, 20) - js := primitive.JavaScript("js") - symbol := primitive.Symbol("sybmol") - binary := primitive.Binary{Subtype: 2, Data: []byte("binary")} - datetime := primitive.NewDateTimeFromTime(now) - regex := primitive.Regex{Pattern: "pattern", Options: "i"} - dbPointer := primitive.DBPointer{DB: "db", Pointer: oid} - timestamp := primitive.Timestamp{T: 5, I: 10} - cws := primitive.CodeWithScope{Code: js, Scope: bson.D{{"x", 1}}} - noReflectionRegistry := bson.NewRegistryBuilder().RegisterCodec(tPrimitiveD, ReflectionFreeDCodec).Build() - docWithAllTypes := primitive.D{ - {"byteSlice", []byte("foobar")}, - {"sliceByteSlice", [][]byte{[]byte("foobar")}}, - {"timeTime", now}, - {"sliceTimeTime", []time.Time{now}}, - {"objectID", oid}, - {"sliceObjectID", []primitive.ObjectID{oid}}, - {"decimal128", d128}, - {"sliceDecimal128", []primitive.Decimal128{d128}}, - {"js", js}, - {"sliceJS", []primitive.JavaScript{js}}, - {"symbol", symbol}, - {"sliceSymbol", []primitive.Symbol{symbol}}, - {"binary", binary}, - {"sliceBinary", []primitive.Binary{binary}}, - {"undefined", primitive.Undefined{}}, - {"sliceUndefined", []primitive.Undefined{{}}}, - {"datetime", datetime}, - {"sliceDateTime", []primitive.DateTime{datetime}}, - {"null", primitive.Null{}}, - {"sliceNull", []primitive.Null{{}}}, - {"regex", regex}, - {"sliceRegex", []primitive.Regex{regex}}, - {"dbPointer", dbPointer}, - {"sliceDBPointer", []primitive.DBPointer{dbPointer}}, - {"timestamp", timestamp}, - {"sliceTimestamp", []primitive.Timestamp{timestamp}}, - {"minKey", primitive.MinKey{}}, - {"sliceMinKey", []primitive.MinKey{{}}}, - {"maxKey", primitive.MaxKey{}}, - {"sliceMaxKey", []primitive.MaxKey{{}}}, - {"cws", cws}, - {"sliceCWS", []primitive.CodeWithScope{cws}}, - {"bool", true}, - {"sliceBool", []bool{true}}, - {"int", int(10)}, - {"sliceInt", []int{10}}, - {"int8", int8(10)}, - {"sliceInt8", []int8{10}}, - {"int16", int16(10)}, - {"sliceInt16", []int16{10}}, - {"int32", int32(10)}, - {"sliceInt32", []int32{10}}, - {"int64", int64(10)}, - {"sliceInt64", []int64{10}}, - {"uint", uint(10)}, - {"sliceUint", []uint{10}}, - {"uint8", uint8(10)}, - {"sliceUint8", []uint8{10}}, - {"uint16", uint16(10)}, - {"sliceUint16", []uint16{10}}, - {"uint32", uint32(10)}, - {"sliceUint32", []uint32{10}}, - {"uint64", uint64(10)}, - {"sliceUint64", []uint64{10}}, - {"float32", float32(10)}, - {"sliceFloat32", []float32{10}}, - {"float64", float64(10)}, - {"sliceFloat64", []float64{10}}, - {"primitiveA", primitive.A{"foo", "bar"}}, - } - - t.Run("encode", func(t *testing.T) { - // Assert that bson.Marshal returns the same result when using the default registry and noReflectionRegistry. - - expected, err := bson.Marshal(docWithAllTypes) - assert.Nil(t, err, "Marshal error with default registry: %v", err) - actual, err := bson.MarshalWithRegistry(noReflectionRegistry, docWithAllTypes) - assert.Nil(t, err, "Marshal error with noReflectionRegistry: %v", err) - assert.Equal(t, expected, actual, "expected doc %s, got %s", bson.Raw(expected), bson.Raw(actual)) - }) - t.Run("decode", func(t *testing.T) { - // Assert that bson.Unmarshal returns the same result when using the default registry and noReflectionRegistry. - - // docWithAllTypes contains some types that can't be roundtripped. For example, any slices besides primitive.A - // would start of as []T but unmarshal to primitive.A. To get around this, we first marshal docWithAllTypes to - // raw bytes and then Unmarshal to another primitive.D rather than asserting directly against docWithAllTypes. - docBytes, err := bson.Marshal(docWithAllTypes) - assert.Nil(t, err, "Marshal error: %v", err) - - var expected, actual primitive.D - err = bson.Unmarshal(docBytes, &expected) - assert.Nil(t, err, "Unmarshal error with default registry: %v", err) - err = bson.UnmarshalWithRegistry(noReflectionRegistry, docBytes, &actual) - assert.Nil(t, err, "Unmarshal error with noReflectionRegistry: %v", err) - - assert.Equal(t, expected, actual, "expected document %v, got %v", expected, actual) - }) -} diff --git a/x/bsonx/registry.go b/x/bsonx/registry.go deleted file mode 100644 index 3ca1c326c8..0000000000 --- a/x/bsonx/registry.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2022-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/bsoncodec" -) - -// DefaultRegistry is the default bsoncodec.Registry. It contains the default codecs and the -// primitive codecs. -var DefaultRegistry = NewRegistryBuilder().Build() - -// NewRegistryBuilder creates a new RegistryBuilder configured with the default encoders and -// decoders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the -// PrimitiveCodecs type in this package. -func NewRegistryBuilder() *bsoncodec.RegistryBuilder { - rb := bsoncodec.NewRegistryBuilder() - bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb) - bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb) - bson.PrimitiveCodecs{}.RegisterPrimitiveCodecs(rb) - primitiveCodecs.RegisterPrimitiveCodecs(rb) - return rb -} diff --git a/x/bsonx/value.go b/x/bsonx/value.go deleted file mode 100644 index f66f6b240f..0000000000 --- a/x/bsonx/value.go +++ /dev/null @@ -1,866 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "math" - "time" - - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" -) - -// Val represents a BSON value. -type Val struct { - // NOTE: The bootstrap is a small amount of space that'll be on the stack. At 15 bytes this - // doesn't make this type any larger, since there are 7 bytes of padding and we want an int64 to - // store small values (e.g. boolean, double, int64, etc...). The primitive property is where all - // of the larger values go. They will use either Go primitives or the primitive.* types. - t bsontype.Type - bootstrap [15]byte - primitive interface{} -} - -func (v Val) string() string { - if v.primitive != nil { - return v.primitive.(string) - } - // The string will either end with a null byte or it fills the entire bootstrap space. - length := v.bootstrap[0] - return string(v.bootstrap[1 : length+1]) -} - -func (v Val) writestring(str string) Val { - switch { - case len(str) < 15: - v.bootstrap[0] = uint8(len(str)) - copy(v.bootstrap[1:], str) - default: - v.primitive = str - } - return v -} - -func (v Val) i64() int64 { - return int64(v.bootstrap[0]) | int64(v.bootstrap[1])<<8 | int64(v.bootstrap[2])<<16 | - int64(v.bootstrap[3])<<24 | int64(v.bootstrap[4])<<32 | int64(v.bootstrap[5])<<40 | - int64(v.bootstrap[6])<<48 | int64(v.bootstrap[7])<<56 -} - -func (v Val) writei64(i64 int64) Val { - v.bootstrap[0] = byte(i64) - v.bootstrap[1] = byte(i64 >> 8) - v.bootstrap[2] = byte(i64 >> 16) - v.bootstrap[3] = byte(i64 >> 24) - v.bootstrap[4] = byte(i64 >> 32) - v.bootstrap[5] = byte(i64 >> 40) - v.bootstrap[6] = byte(i64 >> 48) - v.bootstrap[7] = byte(i64 >> 56) - return v -} - -// IsZero returns true if this value is zero or a BSON null. -func (v Val) IsZero() bool { return v.t == bsontype.Type(0) || v.t == bsontype.Null } - -func (v Val) String() string { - // TODO(GODRIVER-612): When bsoncore has appenders for extended JSON use that here. - return fmt.Sprintf("%v", v.Interface()) -} - -// Interface returns the Go value of this Value as an empty interface. -// -// This method will return nil if it is empty, otherwise it will return a Go primitive or a -// primitive.* instance. -func (v Val) Interface() interface{} { - switch v.Type() { - case bsontype.Double: - return v.Double() - case bsontype.String: - return v.StringValue() - case bsontype.EmbeddedDocument: - switch v.primitive.(type) { - case Doc: - return v.primitive.(Doc) - case MDoc: - return v.primitive.(MDoc) - default: - return primitive.Null{} - } - case bsontype.Array: - return v.Array() - case bsontype.Binary: - return v.primitive.(primitive.Binary) - case bsontype.Undefined: - return primitive.Undefined{} - case bsontype.ObjectID: - return v.ObjectID() - case bsontype.Boolean: - return v.Boolean() - case bsontype.DateTime: - return v.DateTime() - case bsontype.Null: - return primitive.Null{} - case bsontype.Regex: - return v.primitive.(primitive.Regex) - case bsontype.DBPointer: - return v.primitive.(primitive.DBPointer) - case bsontype.JavaScript: - return v.JavaScript() - case bsontype.Symbol: - return v.Symbol() - case bsontype.CodeWithScope: - return v.primitive.(primitive.CodeWithScope) - case bsontype.Int32: - return v.Int32() - case bsontype.Timestamp: - t, i := v.Timestamp() - return primitive.Timestamp{T: t, I: i} - case bsontype.Int64: - return v.Int64() - case bsontype.Decimal128: - return v.Decimal128() - case bsontype.MinKey: - return primitive.MinKey{} - case bsontype.MaxKey: - return primitive.MaxKey{} - default: - return primitive.Null{} - } -} - -// MarshalBSONValue implements the bsoncodec.ValueMarshaler interface. -func (v Val) MarshalBSONValue() (bsontype.Type, []byte, error) { - return v.MarshalAppendBSONValue(nil) -} - -// MarshalAppendBSONValue is similar to MarshalBSONValue, but allows the caller to specify a slice -// to add the bytes to. -func (v Val) MarshalAppendBSONValue(dst []byte) (bsontype.Type, []byte, error) { - t := v.Type() - switch v.Type() { - case bsontype.Double: - dst = bsoncore.AppendDouble(dst, v.Double()) - case bsontype.String: - dst = bsoncore.AppendString(dst, v.String()) - case bsontype.EmbeddedDocument: - switch v.primitive.(type) { - case Doc: - t, dst, _ = v.primitive.(Doc).MarshalBSONValue() // Doc.MarshalBSONValue never returns an error. - case MDoc: - t, dst, _ = v.primitive.(MDoc).MarshalBSONValue() // MDoc.MarshalBSONValue never returns an error. - } - case bsontype.Array: - t, dst, _ = v.Array().MarshalBSONValue() // Arr.MarshalBSON never returns an error. - case bsontype.Binary: - subtype, bindata := v.Binary() - dst = bsoncore.AppendBinary(dst, subtype, bindata) - case bsontype.Undefined: - case bsontype.ObjectID: - dst = bsoncore.AppendObjectID(dst, v.ObjectID()) - case bsontype.Boolean: - dst = bsoncore.AppendBoolean(dst, v.Boolean()) - case bsontype.DateTime: - dst = bsoncore.AppendDateTime(dst, v.DateTime()) - case bsontype.Null: - case bsontype.Regex: - pattern, options := v.Regex() - dst = bsoncore.AppendRegex(dst, pattern, options) - case bsontype.DBPointer: - ns, ptr := v.DBPointer() - dst = bsoncore.AppendDBPointer(dst, ns, ptr) - case bsontype.JavaScript: - dst = bsoncore.AppendJavaScript(dst, v.JavaScript()) - case bsontype.Symbol: - dst = bsoncore.AppendSymbol(dst, v.Symbol()) - case bsontype.CodeWithScope: - code, doc := v.CodeWithScope() - var scope []byte - scope, _ = doc.MarshalBSON() // Doc.MarshalBSON never returns an error. - dst = bsoncore.AppendCodeWithScope(dst, code, scope) - case bsontype.Int32: - dst = bsoncore.AppendInt32(dst, v.Int32()) - case bsontype.Timestamp: - t, i := v.Timestamp() - dst = bsoncore.AppendTimestamp(dst, t, i) - case bsontype.Int64: - dst = bsoncore.AppendInt64(dst, v.Int64()) - case bsontype.Decimal128: - dst = bsoncore.AppendDecimal128(dst, v.Decimal128()) - case bsontype.MinKey: - case bsontype.MaxKey: - default: - panic(fmt.Errorf("invalid BSON type %v", t)) - } - - return t, dst, nil -} - -// UnmarshalBSONValue implements the bsoncodec.ValueUnmarshaler interface. -func (v *Val) UnmarshalBSONValue(t bsontype.Type, data []byte) error { - if v == nil { - return errors.New("cannot unmarshal into nil Value") - } - var err error - var ok = true - var rem []byte - switch t { - case bsontype.Double: - var f64 float64 - f64, rem, ok = bsoncore.ReadDouble(data) - *v = Double(f64) - case bsontype.String: - var str string - str, rem, ok = bsoncore.ReadString(data) - *v = String(str) - case bsontype.EmbeddedDocument: - var raw []byte - var doc Doc - raw, rem, ok = bsoncore.ReadDocument(data) - doc, err = ReadDoc(raw) - *v = Document(doc) - case bsontype.Array: - var raw []byte - arr := make(Arr, 0) - raw, rem, ok = bsoncore.ReadArray(data) - err = arr.UnmarshalBSONValue(t, raw) - *v = Array(arr) - case bsontype.Binary: - var subtype byte - var bindata []byte - subtype, bindata, rem, ok = bsoncore.ReadBinary(data) - *v = Binary(subtype, bindata) - case bsontype.Undefined: - *v = Undefined() - case bsontype.ObjectID: - var oid primitive.ObjectID - oid, rem, ok = bsoncore.ReadObjectID(data) - *v = ObjectID(oid) - case bsontype.Boolean: - var b bool - b, rem, ok = bsoncore.ReadBoolean(data) - *v = Boolean(b) - case bsontype.DateTime: - var dt int64 - dt, rem, ok = bsoncore.ReadDateTime(data) - *v = DateTime(dt) - case bsontype.Null: - *v = Null() - case bsontype.Regex: - var pattern, options string - pattern, options, rem, ok = bsoncore.ReadRegex(data) - *v = Regex(pattern, options) - case bsontype.DBPointer: - var ns string - var ptr primitive.ObjectID - ns, ptr, rem, ok = bsoncore.ReadDBPointer(data) - *v = DBPointer(ns, ptr) - case bsontype.JavaScript: - var js string - js, rem, ok = bsoncore.ReadJavaScript(data) - *v = JavaScript(js) - case bsontype.Symbol: - var symbol string - symbol, rem, ok = bsoncore.ReadSymbol(data) - *v = Symbol(symbol) - case bsontype.CodeWithScope: - var raw []byte - var code string - var scope Doc - code, raw, rem, ok = bsoncore.ReadCodeWithScope(data) - scope, err = ReadDoc(raw) - *v = CodeWithScope(code, scope) - case bsontype.Int32: - var i32 int32 - i32, rem, ok = bsoncore.ReadInt32(data) - *v = Int32(i32) - case bsontype.Timestamp: - var i, t uint32 - t, i, rem, ok = bsoncore.ReadTimestamp(data) - *v = Timestamp(t, i) - case bsontype.Int64: - var i64 int64 - i64, rem, ok = bsoncore.ReadInt64(data) - *v = Int64(i64) - case bsontype.Decimal128: - var d128 primitive.Decimal128 - d128, rem, ok = bsoncore.ReadDecimal128(data) - *v = Decimal128(d128) - case bsontype.MinKey: - *v = MinKey() - case bsontype.MaxKey: - *v = MaxKey() - default: - err = fmt.Errorf("invalid BSON type %v", t) - } - - if !ok && err == nil { - err = bsoncore.NewInsufficientBytesError(data, rem) - } - - return err -} - -// Type returns the BSON type of this value. -func (v Val) Type() bsontype.Type { - if v.t == bsontype.Type(0) { - return bsontype.Null - } - return v.t -} - -// IsNumber returns true if the type of v is a numberic BSON type. -func (v Val) IsNumber() bool { - switch v.Type() { - case bsontype.Double, bsontype.Int32, bsontype.Int64, bsontype.Decimal128: - return true - default: - return false - } -} - -// Double returns the BSON double value the Value represents. It panics if the value is a BSON type -// other than double. -func (v Val) Double() float64 { - if v.t != bsontype.Double { - panic(ElementTypeError{"bson.Value.Double", v.t}) - } - return math.Float64frombits(binary.LittleEndian.Uint64(v.bootstrap[0:8])) -} - -// DoubleOK is the same as Double, but returns a boolean instead of panicking. -func (v Val) DoubleOK() (float64, bool) { - if v.t != bsontype.Double { - return 0, false - } - return math.Float64frombits(binary.LittleEndian.Uint64(v.bootstrap[0:8])), true -} - -// StringValue returns the BSON string the Value represents. It panics if the value is a BSON type -// other than string. -// -// NOTE: This method is called StringValue to avoid it implementing the -// fmt.Stringer interface. -func (v Val) StringValue() string { - if v.t != bsontype.String { - panic(ElementTypeError{"bson.Value.StringValue", v.t}) - } - return v.string() -} - -// StringValueOK is the same as StringValue, but returns a boolean instead of -// panicking. -func (v Val) StringValueOK() (string, bool) { - if v.t != bsontype.String { - return "", false - } - return v.string(), true -} - -func (v Val) asDoc() Doc { - doc, ok := v.primitive.(Doc) - if ok { - return doc - } - mdoc := v.primitive.(MDoc) - for k, v := range mdoc { - doc = append(doc, Elem{k, v}) - } - return doc -} - -func (v Val) asMDoc() MDoc { - mdoc, ok := v.primitive.(MDoc) - if ok { - return mdoc - } - mdoc = make(MDoc) - doc := v.primitive.(Doc) - for _, elem := range doc { - mdoc[elem.Key] = elem.Value - } - return mdoc -} - -// Document returns the BSON embedded document value the Value represents. It panics if the value -// is a BSON type other than embedded document. -func (v Val) Document() Doc { - if v.t != bsontype.EmbeddedDocument { - panic(ElementTypeError{"bson.Value.Document", v.t}) - } - return v.asDoc() -} - -// DocumentOK is the same as Document, except it returns a boolean -// instead of panicking. -func (v Val) DocumentOK() (Doc, bool) { - if v.t != bsontype.EmbeddedDocument { - return nil, false - } - return v.asDoc(), true -} - -// MDocument returns the BSON embedded document value the Value represents. It panics if the value -// is a BSON type other than embedded document. -func (v Val) MDocument() MDoc { - if v.t != bsontype.EmbeddedDocument { - panic(ElementTypeError{"bson.Value.MDocument", v.t}) - } - return v.asMDoc() -} - -// MDocumentOK is the same as Document, except it returns a boolean -// instead of panicking. -func (v Val) MDocumentOK() (MDoc, bool) { - if v.t != bsontype.EmbeddedDocument { - return nil, false - } - return v.asMDoc(), true -} - -// Array returns the BSON array value the Value represents. It panics if the value is a BSON type -// other than array. -func (v Val) Array() Arr { - if v.t != bsontype.Array { - panic(ElementTypeError{"bson.Value.Array", v.t}) - } - return v.primitive.(Arr) -} - -// ArrayOK is the same as Array, except it returns a boolean -// instead of panicking. -func (v Val) ArrayOK() (Arr, bool) { - if v.t != bsontype.Array { - return nil, false - } - return v.primitive.(Arr), true -} - -// Binary returns the BSON binary value the Value represents. It panics if the value is a BSON type -// other than binary. -func (v Val) Binary() (byte, []byte) { - if v.t != bsontype.Binary { - panic(ElementTypeError{"bson.Value.Binary", v.t}) - } - bin := v.primitive.(primitive.Binary) - return bin.Subtype, bin.Data -} - -// BinaryOK is the same as Binary, except it returns a boolean instead of -// panicking. -func (v Val) BinaryOK() (byte, []byte, bool) { - if v.t != bsontype.Binary { - return 0x00, nil, false - } - bin := v.primitive.(primitive.Binary) - return bin.Subtype, bin.Data, true -} - -// Undefined returns the BSON undefined the Value represents. It panics if the value is a BSON type -// other than binary. -func (v Val) Undefined() { - if v.t != bsontype.Undefined { - panic(ElementTypeError{"bson.Value.Undefined", v.t}) - } -} - -// UndefinedOK is the same as Undefined, except it returns a boolean instead of -// panicking. -func (v Val) UndefinedOK() bool { - return v.t == bsontype.Undefined -} - -// ObjectID returns the BSON ObjectID the Value represents. It panics if the value is a BSON type -// other than ObjectID. -func (v Val) ObjectID() primitive.ObjectID { - if v.t != bsontype.ObjectID { - panic(ElementTypeError{"bson.Value.ObjectID", v.t}) - } - var oid primitive.ObjectID - copy(oid[:], v.bootstrap[:12]) - return oid -} - -// ObjectIDOK is the same as ObjectID, except it returns a boolean instead of -// panicking. -func (v Val) ObjectIDOK() (primitive.ObjectID, bool) { - if v.t != bsontype.ObjectID { - return primitive.ObjectID{}, false - } - var oid primitive.ObjectID - copy(oid[:], v.bootstrap[:12]) - return oid, true -} - -// Boolean returns the BSON boolean the Value represents. It panics if the value is a BSON type -// other than boolean. -func (v Val) Boolean() bool { - if v.t != bsontype.Boolean { - panic(ElementTypeError{"bson.Value.Boolean", v.t}) - } - return v.bootstrap[0] == 0x01 -} - -// BooleanOK is the same as Boolean, except it returns a boolean instead of -// panicking. -func (v Val) BooleanOK() (bool, bool) { - if v.t != bsontype.Boolean { - return false, false - } - return v.bootstrap[0] == 0x01, true -} - -// DateTime returns the BSON datetime the Value represents. It panics if the value is a BSON type -// other than datetime. -func (v Val) DateTime() int64 { - if v.t != bsontype.DateTime { - panic(ElementTypeError{"bson.Value.DateTime", v.t}) - } - return v.i64() -} - -// DateTimeOK is the same as DateTime, except it returns a boolean instead of -// panicking. -func (v Val) DateTimeOK() (int64, bool) { - if v.t != bsontype.DateTime { - return 0, false - } - return v.i64(), true -} - -// Time returns the BSON datetime the Value represents as time.Time. It panics if the value is a BSON -// type other than datetime. -func (v Val) Time() time.Time { - if v.t != bsontype.DateTime { - panic(ElementTypeError{"bson.Value.Time", v.t}) - } - i := v.i64() - return time.Unix(i/1000, i%1000*1000000) -} - -// TimeOK is the same as Time, except it returns a boolean instead of -// panicking. -func (v Val) TimeOK() (time.Time, bool) { - if v.t != bsontype.DateTime { - return time.Time{}, false - } - i := v.i64() - return time.Unix(i/1000, i%1000*1000000), true -} - -// Null returns the BSON undefined the Value represents. It panics if the value is a BSON type -// other than binary. -func (v Val) Null() { - if v.t != bsontype.Null && v.t != bsontype.Type(0) { - panic(ElementTypeError{"bson.Value.Null", v.t}) - } -} - -// NullOK is the same as Null, except it returns a boolean instead of -// panicking. -func (v Val) NullOK() bool { - if v.t != bsontype.Null && v.t != bsontype.Type(0) { - return false - } - return true -} - -// Regex returns the BSON regex the Value represents. It panics if the value is a BSON type -// other than regex. -func (v Val) Regex() (pattern, options string) { - if v.t != bsontype.Regex { - panic(ElementTypeError{"bson.Value.Regex", v.t}) - } - regex := v.primitive.(primitive.Regex) - return regex.Pattern, regex.Options -} - -// RegexOK is the same as Regex, except that it returns a boolean -// instead of panicking. -func (v Val) RegexOK() (pattern, options string, ok bool) { - if v.t != bsontype.Regex { - return "", "", false - } - regex := v.primitive.(primitive.Regex) - return regex.Pattern, regex.Options, true -} - -// DBPointer returns the BSON dbpointer the Value represents. It panics if the value is a BSON type -// other than dbpointer. -func (v Val) DBPointer() (string, primitive.ObjectID) { - if v.t != bsontype.DBPointer { - panic(ElementTypeError{"bson.Value.DBPointer", v.t}) - } - dbptr := v.primitive.(primitive.DBPointer) - return dbptr.DB, dbptr.Pointer -} - -// DBPointerOK is the same as DBPoitner, except that it returns a boolean -// instead of panicking. -func (v Val) DBPointerOK() (string, primitive.ObjectID, bool) { - if v.t != bsontype.DBPointer { - return "", primitive.ObjectID{}, false - } - dbptr := v.primitive.(primitive.DBPointer) - return dbptr.DB, dbptr.Pointer, true -} - -// JavaScript returns the BSON JavaScript the Value represents. It panics if the value is a BSON type -// other than JavaScript. -func (v Val) JavaScript() string { - if v.t != bsontype.JavaScript { - panic(ElementTypeError{"bson.Value.JavaScript", v.t}) - } - return v.string() -} - -// JavaScriptOK is the same as Javascript, except that it returns a boolean -// instead of panicking. -func (v Val) JavaScriptOK() (string, bool) { - if v.t != bsontype.JavaScript { - return "", false - } - return v.string(), true -} - -// Symbol returns the BSON symbol the Value represents. It panics if the value is a BSON type -// other than symbol. -func (v Val) Symbol() string { - if v.t != bsontype.Symbol { - panic(ElementTypeError{"bson.Value.Symbol", v.t}) - } - return v.string() -} - -// SymbolOK is the same as Javascript, except that it returns a boolean -// instead of panicking. -func (v Val) SymbolOK() (string, bool) { - if v.t != bsontype.Symbol { - return "", false - } - return v.string(), true -} - -// CodeWithScope returns the BSON code with scope value the Value represents. It panics if the -// value is a BSON type other than code with scope. -func (v Val) CodeWithScope() (string, Doc) { - if v.t != bsontype.CodeWithScope { - panic(ElementTypeError{"bson.Value.CodeWithScope", v.t}) - } - cws := v.primitive.(primitive.CodeWithScope) - return string(cws.Code), cws.Scope.(Doc) -} - -// CodeWithScopeOK is the same as JavascriptWithScope, -// except that it returns a boolean instead of panicking. -func (v Val) CodeWithScopeOK() (string, Doc, bool) { - if v.t != bsontype.CodeWithScope { - return "", nil, false - } - cws := v.primitive.(primitive.CodeWithScope) - return string(cws.Code), cws.Scope.(Doc), true -} - -// Int32 returns the BSON int32 the Value represents. It panics if the value is a BSON type -// other than int32. -func (v Val) Int32() int32 { - if v.t != bsontype.Int32 { - panic(ElementTypeError{"bson.Value.Int32", v.t}) - } - return int32(v.bootstrap[0]) | int32(v.bootstrap[1])<<8 | - int32(v.bootstrap[2])<<16 | int32(v.bootstrap[3])<<24 -} - -// Int32OK is the same as Int32, except that it returns a boolean instead of -// panicking. -func (v Val) Int32OK() (int32, bool) { - if v.t != bsontype.Int32 { - return 0, false - } - return int32(v.bootstrap[0]) | int32(v.bootstrap[1])<<8 | - int32(v.bootstrap[2])<<16 | int32(v.bootstrap[3])<<24, - true -} - -// Timestamp returns the BSON timestamp the Value represents. It panics if the value is a -// BSON type other than timestamp. -func (v Val) Timestamp() (t, i uint32) { - if v.t != bsontype.Timestamp { - panic(ElementTypeError{"bson.Value.Timestamp", v.t}) - } - return uint32(v.bootstrap[4]) | uint32(v.bootstrap[5])<<8 | - uint32(v.bootstrap[6])<<16 | uint32(v.bootstrap[7])<<24, - uint32(v.bootstrap[0]) | uint32(v.bootstrap[1])<<8 | - uint32(v.bootstrap[2])<<16 | uint32(v.bootstrap[3])<<24 -} - -// TimestampOK is the same as Timestamp, except that it returns a boolean -// instead of panicking. -func (v Val) TimestampOK() (t uint32, i uint32, ok bool) { - if v.t != bsontype.Timestamp { - return 0, 0, false - } - return uint32(v.bootstrap[4]) | uint32(v.bootstrap[5])<<8 | - uint32(v.bootstrap[6])<<16 | uint32(v.bootstrap[7])<<24, - uint32(v.bootstrap[0]) | uint32(v.bootstrap[1])<<8 | - uint32(v.bootstrap[2])<<16 | uint32(v.bootstrap[3])<<24, - true -} - -// Int64 returns the BSON int64 the Value represents. It panics if the value is a BSON type -// other than int64. -func (v Val) Int64() int64 { - if v.t != bsontype.Int64 { - panic(ElementTypeError{"bson.Value.Int64", v.t}) - } - return v.i64() -} - -// Int64OK is the same as Int64, except that it returns a boolean instead of -// panicking. -func (v Val) Int64OK() (int64, bool) { - if v.t != bsontype.Int64 { - return 0, false - } - return v.i64(), true -} - -// Decimal128 returns the BSON decimal128 value the Value represents. It panics if the value is a -// BSON type other than decimal128. -func (v Val) Decimal128() primitive.Decimal128 { - if v.t != bsontype.Decimal128 { - panic(ElementTypeError{"bson.Value.Decimal128", v.t}) - } - return v.primitive.(primitive.Decimal128) -} - -// Decimal128OK is the same as Decimal128, except that it returns a boolean -// instead of panicking. -func (v Val) Decimal128OK() (primitive.Decimal128, bool) { - if v.t != bsontype.Decimal128 { - return primitive.Decimal128{}, false - } - return v.primitive.(primitive.Decimal128), true -} - -// MinKey returns the BSON minkey the Value represents. It panics if the value is a BSON type -// other than binary. -func (v Val) MinKey() { - if v.t != bsontype.MinKey { - panic(ElementTypeError{"bson.Value.MinKey", v.t}) - } -} - -// MinKeyOK is the same as MinKey, except it returns a boolean instead of -// panicking. -func (v Val) MinKeyOK() bool { - return v.t == bsontype.MinKey -} - -// MaxKey returns the BSON maxkey the Value represents. It panics if the value is a BSON type -// other than binary. -func (v Val) MaxKey() { - if v.t != bsontype.MaxKey { - panic(ElementTypeError{"bson.Value.MaxKey", v.t}) - } -} - -// MaxKeyOK is the same as MaxKey, except it returns a boolean instead of -// panicking. -func (v Val) MaxKeyOK() bool { - return v.t == bsontype.MaxKey -} - -// Equal compares v to v2 and returns true if they are equal. Unknown BSON types are -// never equal. Two empty values are equal. -func (v Val) Equal(v2 Val) bool { - if v.Type() != v2.Type() { - return false - } - if v.IsZero() && v2.IsZero() { - return true - } - - switch v.Type() { - case bsontype.Double, bsontype.DateTime, bsontype.Timestamp, bsontype.Int64: - return bytes.Equal(v.bootstrap[0:8], v2.bootstrap[0:8]) - case bsontype.String: - return v.string() == v2.string() - case bsontype.EmbeddedDocument: - return v.equalDocs(v2) - case bsontype.Array: - return v.Array().Equal(v2.Array()) - case bsontype.Binary: - return v.primitive.(primitive.Binary).Equal(v2.primitive.(primitive.Binary)) - case bsontype.Undefined: - return true - case bsontype.ObjectID: - return bytes.Equal(v.bootstrap[0:12], v2.bootstrap[0:12]) - case bsontype.Boolean: - return v.bootstrap[0] == v2.bootstrap[0] - case bsontype.Null: - return true - case bsontype.Regex: - return v.primitive.(primitive.Regex).Equal(v2.primitive.(primitive.Regex)) - case bsontype.DBPointer: - return v.primitive.(primitive.DBPointer).Equal(v2.primitive.(primitive.DBPointer)) - case bsontype.JavaScript: - return v.JavaScript() == v2.JavaScript() - case bsontype.Symbol: - return v.Symbol() == v2.Symbol() - case bsontype.CodeWithScope: - code1, scope1 := v.primitive.(primitive.CodeWithScope).Code, v.primitive.(primitive.CodeWithScope).Scope - code2, scope2 := v2.primitive.(primitive.CodeWithScope).Code, v2.primitive.(primitive.CodeWithScope).Scope - return code1 == code2 && v.equalInterfaceDocs(scope1, scope2) - case bsontype.Int32: - return v.Int32() == v2.Int32() - case bsontype.Decimal128: - h, l := v.Decimal128().GetBytes() - h2, l2 := v2.Decimal128().GetBytes() - return h == h2 && l == l2 - case bsontype.MinKey: - return true - case bsontype.MaxKey: - return true - default: - return false - } -} - -func (v Val) equalDocs(v2 Val) bool { - _, ok1 := v.primitive.(MDoc) - _, ok2 := v2.primitive.(MDoc) - if ok1 || ok2 { - return v.asMDoc().Equal(v2.asMDoc()) - } - return v.asDoc().Equal(v2.asDoc()) -} - -func (Val) equalInterfaceDocs(i, i2 interface{}) bool { - switch d := i.(type) { - case MDoc: - d2, ok := i2.(IDoc) - if !ok { - return false - } - return d.Equal(d2) - case Doc: - d2, ok := i2.(IDoc) - if !ok { - return false - } - return d.Equal(d2) - case nil: - return i2 == nil - default: - return false - } -} diff --git a/x/bsonx/value_test.go b/x/bsonx/value_test.go deleted file mode 100644 index 1f8b2d91f3..0000000000 --- a/x/bsonx/value_test.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package bsonx - -import ( - "reflect" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "go.mongodb.org/mongo-driver/bson/bsontype" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -func TestValue(t *testing.T) { - longstr := "foobarbazqux, hello, world!" - bytestr14 := "fourteen bytes" - bin := primitive.Binary{Subtype: 0xFF, Data: []byte{0x01, 0x02, 0x03}} - oid := primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} - now := time.Now().Truncate(time.Millisecond) - nowdt := now.Unix()*1e3 + int64(now.Nanosecond()/1e6) - regex := primitive.Regex{Pattern: "/foobarbaz/", Options: "abr"} - dbptr := primitive.DBPointer{DB: "foobar", Pointer: oid} - js := "var hello ='world';" - symbol := "foobarbaz" - cws := primitive.CodeWithScope{Code: primitive.JavaScript(js), Scope: Doc{}} - code, scope := js, Doc{} - ts := primitive.Timestamp{I: 12345, T: 67890} - d128 := primitive.NewDecimal128(12345, 67890) - - t.Parallel() - testCases := []struct { - name string - fn interface{} // method to call - ret []interface{} // return value - err interface{} // panic result or bool - }{ - {"Interface/Double", Double(3.14159).Interface, []interface{}{float64(3.14159)}, nil}, - {"Interface/String", String("foo").Interface, []interface{}{"foo"}, nil}, - {"Interface/Document", Document(Doc{}).Interface, []interface{}{Doc{}}, nil}, - {"Interface/Array", Array(Arr{}).Interface, []interface{}{Arr{}}, nil}, - {"Interface/Binary", Binary(bin.Subtype, bin.Data).Interface, []interface{}{bin}, nil}, - {"Interface/Undefined", Undefined().Interface, []interface{}{primitive.Undefined{}}, nil}, - {"Interface/Null", Null().Interface, []interface{}{primitive.Null{}}, nil}, - {"Interface/ObjectID", ObjectID(oid).Interface, []interface{}{oid}, nil}, - {"Interface/Boolean", Boolean(true).Interface, []interface{}{bool(true)}, nil}, - {"Interface/DateTime", DateTime(1234567890).Interface, []interface{}{int64(1234567890)}, nil}, - {"Interface/Time", Time(now).Interface, []interface{}{nowdt}, nil}, - {"Interface/Regex", Regex(regex.Pattern, regex.Options).Interface, []interface{}{regex}, nil}, - {"Interface/DBPointer", DBPointer(dbptr.DB, dbptr.Pointer).Interface, []interface{}{dbptr}, nil}, - {"Interface/JavaScript", JavaScript(js).Interface, []interface{}{js}, nil}, - {"Interface/Symbol", Symbol(symbol).Interface, []interface{}{symbol}, nil}, - {"Interface/CodeWithScope", CodeWithScope(string(cws.Code), cws.Scope.(Doc)).Interface, []interface{}{cws}, nil}, - {"Interface/Int32", Int32(12345).Interface, []interface{}{int32(12345)}, nil}, - {"Interface/Timestamp", Timestamp(ts.T, ts.I).Interface, []interface{}{ts}, nil}, - {"Interface/Int64", Int64(1234567890).Interface, []interface{}{int64(1234567890)}, nil}, - {"Interface/Decimal128", Decimal128(d128).Interface, []interface{}{d128}, nil}, - {"Interface/MinKey", MinKey().Interface, []interface{}{primitive.MinKey{}}, nil}, - {"Interface/MaxKey", MaxKey().Interface, []interface{}{primitive.MaxKey{}}, nil}, - {"Interface/Empty", Val{}.Interface, []interface{}{primitive.Null{}}, nil}, - {"IsNumber/Double", Double(0).IsNumber, []interface{}{bool(true)}, nil}, - {"IsNumber/Int32", Int32(0).IsNumber, []interface{}{bool(true)}, nil}, - {"IsNumber/Int64", Int64(0).IsNumber, []interface{}{bool(true)}, nil}, - {"IsNumber/Decimal128", Decimal128(primitive.Decimal128{}).IsNumber, []interface{}{bool(true)}, nil}, - {"IsNumber/String", String("").IsNumber, []interface{}{bool(false)}, nil}, - {"Double/panic", String("").Double, nil, ElementTypeError{"bson.Value.Double", bsontype.String}}, - {"Double/success", Double(3.14159).Double, []interface{}{float64(3.14159)}, nil}, - {"DoubleOK/error", String("").DoubleOK, []interface{}{float64(0), false}, nil}, - {"DoubleOK/success", Double(3.14159).DoubleOK, []interface{}{float64(3.14159), true}, nil}, - {"String/panic", Double(0).StringValue, nil, ElementTypeError{"bson.Value.StringValue", bsontype.Double}}, - {"String/success", String("bar").StringValue, []interface{}{"bar"}, nil}, - {"String/14bytes", String(bytestr14).StringValue, []interface{}{bytestr14}, nil}, - {"String/success(long)", String(longstr).StringValue, []interface{}{longstr}, nil}, - {"StringOK/error", Double(0).StringValueOK, []interface{}{"", false}, nil}, - {"StringOK/success", String("bar").StringValueOK, []interface{}{"bar", true}, nil}, - {"Document/panic", Double(0).Document, nil, ElementTypeError{"bson.Value.Document", bsontype.Double}}, - {"Document/success", Document(Doc{}).Document, []interface{}{Doc{}}, nil}, - {"DocumentOK/error", Double(0).DocumentOK, []interface{}{(Doc)(nil), false}, nil}, - {"DocumentOK/success", Document(Doc{}).DocumentOK, []interface{}{Doc{}, true}, nil}, - {"MDocument/panic", Double(0).MDocument, nil, ElementTypeError{"bson.Value.MDocument", bsontype.Double}}, - {"MDocument/success", Document(MDoc{}).MDocument, []interface{}{MDoc{}}, nil}, - {"MDocumentOK/error", Double(0).MDocumentOK, []interface{}{(MDoc)(nil), false}, nil}, - {"MDocumentOK/success", Document(MDoc{}).MDocumentOK, []interface{}{MDoc{}, true}, nil}, - {"Document->MDocument/success", Document(Doc{}).MDocument, []interface{}{MDoc{}}, nil}, - {"MDocument->Document/success", Document(MDoc{}).Document, []interface{}{Doc{}}, nil}, - {"Document->MDocumentOK/success", Document(Doc{}).MDocumentOK, []interface{}{MDoc{}, true}, nil}, - {"MDocument->DocumentOK/success", Document(MDoc{}).DocumentOK, []interface{}{Doc{}, true}, nil}, - {"Array/panic", Double(0).Array, nil, ElementTypeError{"bson.Value.Array", bsontype.Double}}, - {"Array/success", Array(Arr{}).Array, []interface{}{Arr{}}, nil}, - {"ArrayOK/error", Double(0).ArrayOK, []interface{}{(Arr)(nil), false}, nil}, - {"ArrayOK/success", Array(Arr{}).ArrayOK, []interface{}{Arr{}, true}, nil}, - {"Document/NilDocument", Document((Doc)(nil)).Interface, []interface{}{primitive.Null{}}, nil}, - {"Array/NilArray", Array((Arr)(nil)).Interface, []interface{}{primitive.Null{}}, nil}, - {"Document/Nil", Document(nil).Interface, []interface{}{primitive.Null{}}, nil}, - {"Array/Nil", Array(nil).Interface, []interface{}{primitive.Null{}}, nil}, - {"Binary/panic", Double(0).Binary, nil, ElementTypeError{"bson.Value.Binary", bsontype.Double}}, - {"Binary/success", Binary(bin.Subtype, bin.Data).Binary, []interface{}{bin.Subtype, bin.Data}, nil}, - {"BinaryOK/error", Double(0).BinaryOK, []interface{}{byte(0x00), []byte(nil), false}, nil}, - {"BinaryOK/success", Binary(bin.Subtype, bin.Data).BinaryOK, []interface{}{bin.Subtype, bin.Data, true}, nil}, - {"Undefined/panic", Double(0).Undefined, nil, ElementTypeError{"bson.Value.Undefined", bsontype.Double}}, - {"Undefined/success", Undefined().Undefined, nil, nil}, - {"UndefinedOK/error", Double(0).UndefinedOK, []interface{}{false}, nil}, - {"UndefinedOK/success", Undefined().UndefinedOK, []interface{}{true}, nil}, - {"ObjectID/panic", Double(0).ObjectID, nil, ElementTypeError{"bson.Value.ObjectID", bsontype.Double}}, - {"ObjectID/success", ObjectID(oid).ObjectID, []interface{}{oid}, nil}, - {"ObjectIDOK/error", Double(0).ObjectIDOK, []interface{}{primitive.ObjectID{}, false}, nil}, - {"ObjectIDOK/success", ObjectID(oid).ObjectIDOK, []interface{}{oid, true}, nil}, - {"Boolean/panic", Double(0).Boolean, nil, ElementTypeError{"bson.Value.Boolean", bsontype.Double}}, - {"Boolean/success", Boolean(true).Boolean, []interface{}{bool(true)}, nil}, - {"BooleanOK/error", Double(0).BooleanOK, []interface{}{bool(false), false}, nil}, - {"BooleanOK/success", Boolean(false).BooleanOK, []interface{}{false, true}, nil}, - {"DateTime/panic", Double(0).DateTime, nil, ElementTypeError{"bson.Value.DateTime", bsontype.Double}}, - {"DateTime/success", DateTime(1234567890).DateTime, []interface{}{int64(1234567890)}, nil}, - {"DateTimeOK/error", Double(0).DateTimeOK, []interface{}{int64(0), false}, nil}, - {"DateTimeOK/success", DateTime(987654321).DateTimeOK, []interface{}{int64(987654321), true}, nil}, - {"Time/panic", Double(0).Time, nil, ElementTypeError{"bson.Value.Time", bsontype.Double}}, - {"Time/success", Time(now).Time, []interface{}{now}, nil}, - {"TimeOK/error", Double(0).TimeOK, []interface{}{time.Time{}, false}, nil}, - {"TimeOK/success", Time(now).TimeOK, []interface{}{now, true}, nil}, - {"Time->DateTime", Time(now).DateTime, []interface{}{nowdt}, nil}, - {"DateTime->Time", DateTime(nowdt).Time, []interface{}{now}, nil}, - {"Null/panic", Double(0).Null, nil, ElementTypeError{"bson.Value.Null", bsontype.Double}}, - {"Null/success", Null().Null, nil, nil}, - {"NullOK/error", Double(0).NullOK, []interface{}{false}, nil}, - {"NullOK/success", Null().NullOK, []interface{}{true}, nil}, - {"Regex/panic", Double(0).Regex, nil, ElementTypeError{"bson.Value.Regex", bsontype.Double}}, - {"Regex/success", Regex(regex.Pattern, regex.Options).Regex, []interface{}{regex.Pattern, regex.Options}, nil}, - {"RegexOK/error", Double(0).RegexOK, []interface{}{"", "", false}, nil}, - {"RegexOK/success", Regex(regex.Pattern, regex.Options).RegexOK, []interface{}{regex.Pattern, regex.Options, true}, nil}, - {"DBPointer/panic", Double(0).DBPointer, nil, ElementTypeError{"bson.Value.DBPointer", bsontype.Double}}, - {"DBPointer/success", DBPointer(dbptr.DB, dbptr.Pointer).DBPointer, []interface{}{dbptr.DB, dbptr.Pointer}, nil}, - {"DBPointerOK/error", Double(0).DBPointerOK, []interface{}{"", primitive.ObjectID{}, false}, nil}, - {"DBPointerOK/success", DBPointer(dbptr.DB, dbptr.Pointer).DBPointerOK, []interface{}{dbptr.DB, dbptr.Pointer, true}, nil}, - {"JavaScript/panic", Double(0).JavaScript, nil, ElementTypeError{"bson.Value.JavaScript", bsontype.Double}}, - {"JavaScript/success", JavaScript(js).JavaScript, []interface{}{js}, nil}, - {"JavaScriptOK/error", Double(0).JavaScriptOK, []interface{}{"", false}, nil}, - {"JavaScriptOK/success", JavaScript(js).JavaScriptOK, []interface{}{js, true}, nil}, - {"Symbol/panic", Double(0).Symbol, nil, ElementTypeError{"bson.Value.Symbol", bsontype.Double}}, - {"Symbol/success", Symbol(symbol).Symbol, []interface{}{symbol}, nil}, - {"SymbolOK/error", Double(0).SymbolOK, []interface{}{"", false}, nil}, - {"SymbolOK/success", Symbol(symbol).SymbolOK, []interface{}{symbol, true}, nil}, - {"CodeWithScope/panic", Double(0).CodeWithScope, nil, ElementTypeError{"bson.Value.CodeWithScope", bsontype.Double}}, - {"CodeWithScope/success", CodeWithScope(code, scope).CodeWithScope, []interface{}{code, scope}, nil}, - {"CodeWithScopeOK/error", Double(0).CodeWithScopeOK, []interface{}{"", (Doc)(nil), false}, nil}, - {"CodeWithScopeOK/success", CodeWithScope(code, scope).CodeWithScopeOK, []interface{}{code, scope, true}, nil}, - {"Int32/panic", Double(0).Int32, nil, ElementTypeError{"bson.Value.Int32", bsontype.Double}}, - {"Int32/success", Int32(12345).Int32, []interface{}{int32(12345)}, nil}, - {"Int32OK/error", Double(0).Int32OK, []interface{}{int32(0), false}, nil}, - {"Int32OK/success", Int32(54321).Int32OK, []interface{}{int32(54321), true}, nil}, - {"Timestamp/panic", Double(0).Timestamp, nil, ElementTypeError{"bson.Value.Timestamp", bsontype.Double}}, - {"Timestamp/success", Timestamp(ts.T, ts.I).Timestamp, []interface{}{ts.T, ts.I}, nil}, - {"TimestampOK/error", Double(0).TimestampOK, []interface{}{uint32(0), uint32(0), false}, nil}, - {"TimestampOK/success", Timestamp(ts.T, ts.I).TimestampOK, []interface{}{ts.T, ts.I, true}, nil}, - {"Int64/panic", Double(0).Int64, nil, ElementTypeError{"bson.Value.Int64", bsontype.Double}}, - {"Int64/success", Int64(1234567890).Int64, []interface{}{int64(1234567890)}, nil}, - {"Int64OK/error", Double(0).Int64OK, []interface{}{int64(0), false}, nil}, - {"Int64OK/success", Int64(9876543210).Int64OK, []interface{}{int64(9876543210), true}, nil}, - {"Decimal128/panic", Double(0).Decimal128, nil, ElementTypeError{"bson.Value.Decimal128", bsontype.Double}}, - {"Decimal128/success", Decimal128(d128).Decimal128, []interface{}{d128}, nil}, - {"Decimal128OK/error", Double(0).Decimal128OK, []interface{}{primitive.Decimal128{}, false}, nil}, - {"Decimal128OK/success", Decimal128(d128).Decimal128OK, []interface{}{d128, true}, nil}, - {"MinKey/panic", Double(0).MinKey, nil, ElementTypeError{"bson.Value.MinKey", bsontype.Double}}, - {"MinKey/success", MinKey().MinKey, nil, nil}, - {"MinKeyOK/error", Double(0).MinKeyOK, []interface{}{false}, nil}, - {"MinKeyOK/success", MinKey().MinKeyOK, []interface{}{true}, nil}, - {"MaxKey/panic", Double(0).MaxKey, nil, ElementTypeError{"bson.Value.MaxKey", bsontype.Double}}, - {"MaxKey/success", MaxKey().MaxKey, nil, nil}, - {"MaxKeyOK/error", Double(0).MaxKeyOK, []interface{}{false}, nil}, - {"MaxKeyOK/success", MaxKey().MaxKeyOK, []interface{}{true}, nil}, - } - - for _, tc := range testCases { - tc := tc // capture range variable - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - defer func() { - err := recover() - if err != nil && !cmp.Equal(err, tc.err) { - t.Errorf("panic errors are not equal. got %v; want %v", err, tc.err) - if tc.err == nil { - panic(err) - } - } - }() - fn := reflect.ValueOf(tc.fn) - if fn.Kind() != reflect.Func { - t.Fatalf("fn must be a function, but is a %s", fn.Kind()) - } - ret := fn.Call(nil) - if len(ret) != len(tc.ret) { - t.Fatalf("number of returned values does not match. got %d; want %d", len(ret), len(tc.ret)) - } - - for idx := range ret { - got, want := ret[idx].Interface(), tc.ret[idx] - if !cmp.Equal(got, want, cmp.Comparer(compareDecimal128)) { - t.Errorf("Return %d does not match. got %v; want %v", idx, got, want) - } - } - }) - } - - t.Run("Equal", func(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - v1 Val - v2 Val - res bool - }{ - {"Different Types", String(""), Double(0), false}, - {"Unknown Types", Val{t: bsontype.Type(0x77)}, Val{t: bsontype.Type(0x77)}, false}, - {"Empty Types", Val{}, Val{}, true}, - {"Double/Equal", Double(3.14159), Double(3.14159), true}, - {"Double/Not Equal", Double(3.14159), Double(9.51413), false}, - {"DateTime/Equal", DateTime(nowdt), DateTime(nowdt), true}, - {"DateTime/Not Equal", DateTime(nowdt), DateTime(0), false}, - {"String/Equal", String("hello"), String("hello"), true}, - {"String/Not Equal", String("hello"), String("world"), false}, - {"Document/Equal", Document(Doc{}), Document(Doc{}), true}, - {"Document/Not Equal", Document(Doc{}), Document(Doc{{"", Null()}}), false}, - {"Array/Equal", Array(Arr{}), Array(Arr{}), true}, - {"Array/Not Equal", Array(Arr{}), Array(Arr{Null()}), false}, - {"Binary/Equal", Binary(bin.Subtype, bin.Data), Binary(bin.Subtype, bin.Data), true}, - {"Binary/Not Equal", Binary(bin.Subtype, bin.Data), Binary(0x00, nil), false}, - {"Undefined/Equal", Undefined(), Undefined(), true}, - {"ObjectID/Equal", ObjectID(oid), ObjectID(oid), true}, - {"ObjectID/Not Equal", ObjectID(oid), ObjectID(primitive.ObjectID{}), false}, - {"Boolean/Equal", Boolean(true), Boolean(true), true}, - {"Boolean/Not Equal", Boolean(true), Boolean(false), false}, - {"Null/Equal", Null(), Null(), true}, - {"Regex/Equal", Regex(regex.Pattern, regex.Options), Regex(regex.Pattern, regex.Options), true}, - {"Regex/Not Equal", Regex(regex.Pattern, regex.Options), Regex("", ""), false}, - {"DBPointer/Equal", DBPointer(dbptr.DB, dbptr.Pointer), DBPointer(dbptr.DB, dbptr.Pointer), true}, - {"DBPointer/Not Equal", DBPointer(dbptr.DB, dbptr.Pointer), DBPointer("", primitive.ObjectID{}), false}, - {"JavaScript/Equal", JavaScript(js), JavaScript(js), true}, - {"JavaScript/Not Equal", JavaScript(js), JavaScript(""), false}, - {"Symbol/Equal", Symbol(symbol), Symbol(symbol), true}, - {"Symbol/Not Equal", Symbol(symbol), Symbol(""), false}, - {"CodeWithScope/Equal", CodeWithScope(code, scope), CodeWithScope(code, scope), true}, - {"CodeWithScope/Equal (equal scope)", CodeWithScope(code, scope), CodeWithScope(code, Doc{}), true}, - {"CodeWithScope/Not Equal", CodeWithScope(code, scope), CodeWithScope("", nil), false}, - {"Int32/Equal", Int32(12345), Int32(12345), true}, - {"Int32/Not Equal", Int32(12345), Int32(54321), false}, - {"Timestamp/Equal", Timestamp(ts.T, ts.I), Timestamp(ts.T, ts.I), true}, - {"Timestamp/Not Equal", Timestamp(ts.T, ts.I), Timestamp(0, 0), false}, - {"Int64/Equal", Int64(1234567890), Int64(1234567890), true}, - {"Int64/Not Equal", Int64(1234567890), Int64(9876543210), false}, - {"Decimal128/Equal", Decimal128(d128), Decimal128(d128), true}, - {"Decimal128/Not Equal", Decimal128(d128), Decimal128(primitive.Decimal128{}), false}, - {"MinKey/Equal", MinKey(), MinKey(), true}, - {"MaxKey/Equal", MaxKey(), MaxKey(), true}, - } - - for _, tc := range testCases { - tc := tc // capture range variable - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - res := tc.v1.Equal(tc.v2) - if res != tc.res { - t.Errorf("results do not match. got %v; want %v", res, tc.res) - } - }) - } - }) -}