diff options
Diffstat (limited to 'vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go')
-rw-r--r-- | vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go new file mode 100644 index 000000000..969570424 --- /dev/null +++ b/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_wrappers.go @@ -0,0 +1,492 @@ +// 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 bsonrw + +import ( + "encoding/base64" + "errors" + "fmt" + "math" + "strconv" + "time" + + "go.mongodb.org/mongo-driver/bson/bsontype" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +func wrapperKeyBSONType(key string) bsontype.Type { + switch key { + case "$numberInt": + return bsontype.Int32 + case "$numberLong": + return bsontype.Int64 + case "$oid": + return bsontype.ObjectID + case "$symbol": + return bsontype.Symbol + case "$numberDouble": + return bsontype.Double + case "$numberDecimal": + return bsontype.Decimal128 + case "$binary": + return bsontype.Binary + case "$code": + return bsontype.JavaScript + case "$scope": + return bsontype.CodeWithScope + case "$timestamp": + return bsontype.Timestamp + case "$regularExpression": + return bsontype.Regex + case "$dbPointer": + return bsontype.DBPointer + case "$date": + return bsontype.DateTime + case "$minKey": + return bsontype.MinKey + case "$maxKey": + return bsontype.MaxKey + case "$undefined": + return bsontype.Undefined + } + + return bsontype.EmbeddedDocument +} + +func (ejv *extJSONValue) parseBinary() (b []byte, subType byte, err error) { + if ejv.t != bsontype.EmbeddedDocument { + return nil, 0, fmt.Errorf("$binary value should be object, but instead is %s", ejv.t) + } + + binObj := ejv.v.(*extJSONObject) + bFound := false + stFound := false + + for i, key := range binObj.keys { + val := binObj.values[i] + + switch key { + case "base64": + if bFound { + return nil, 0, errors.New("duplicate base64 key in $binary") + } + + if val.t != bsontype.String { + return nil, 0, fmt.Errorf("$binary base64 value should be string, but instead is %s", val.t) + } + + base64Bytes, err := base64.StdEncoding.DecodeString(val.v.(string)) + if err != nil { + return nil, 0, fmt.Errorf("invalid $binary base64 string: %s", val.v.(string)) + } + + b = base64Bytes + bFound = true + case "subType": + if stFound { + return nil, 0, errors.New("duplicate subType key in $binary") + } + + if val.t != bsontype.String { + return nil, 0, fmt.Errorf("$binary subType value should be string, but instead is %s", val.t) + } + + i, err := strconv.ParseInt(val.v.(string), 16, 64) + if err != nil { + return nil, 0, fmt.Errorf("invalid $binary subType string: %s", val.v.(string)) + } + + subType = byte(i) + stFound = true + default: + return nil, 0, fmt.Errorf("invalid key in $binary object: %s", key) + } + } + + if !bFound { + return nil, 0, errors.New("missing base64 field in $binary object") + } + + if !stFound { + return nil, 0, errors.New("missing subType field in $binary object") + + } + + return b, subType, nil +} + +func (ejv *extJSONValue) parseDBPointer() (ns string, oid primitive.ObjectID, err error) { + if ejv.t != bsontype.EmbeddedDocument { + return "", primitive.NilObjectID, fmt.Errorf("$dbPointer value should be object, but instead is %s", ejv.t) + } + + dbpObj := ejv.v.(*extJSONObject) + oidFound := false + nsFound := false + + for i, key := range dbpObj.keys { + val := dbpObj.values[i] + + switch key { + case "$ref": + if nsFound { + return "", primitive.NilObjectID, errors.New("duplicate $ref key in $dbPointer") + } + + if val.t != bsontype.String { + return "", primitive.NilObjectID, fmt.Errorf("$dbPointer $ref value should be string, but instead is %s", val.t) + } + + ns = val.v.(string) + nsFound = true + case "$id": + if oidFound { + return "", primitive.NilObjectID, errors.New("duplicate $id key in $dbPointer") + } + + if val.t != bsontype.String { + return "", primitive.NilObjectID, fmt.Errorf("$dbPointer $id value should be string, but instead is %s", val.t) + } + + oid, err = primitive.ObjectIDFromHex(val.v.(string)) + if err != nil { + return "", primitive.NilObjectID, err + } + + oidFound = true + default: + return "", primitive.NilObjectID, fmt.Errorf("invalid key in $dbPointer object: %s", key) + } + } + + if !nsFound { + return "", oid, errors.New("missing $ref field in $dbPointer object") + } + + if !oidFound { + return "", oid, errors.New("missing $id field in $dbPointer object") + } + + return ns, oid, nil +} + +const ( + rfc3339Milli = "2006-01-02T15:04:05.999Z07:00" +) + +var ( + timeFormats = []string{rfc3339Milli, "2006-01-02T15:04:05.999Z0700"} +) + +func (ejv *extJSONValue) parseDateTime() (int64, error) { + switch ejv.t { + case bsontype.Int32: + return int64(ejv.v.(int32)), nil + case bsontype.Int64: + return ejv.v.(int64), nil + case bsontype.String: + return parseDatetimeString(ejv.v.(string)) + case bsontype.EmbeddedDocument: + return parseDatetimeObject(ejv.v.(*extJSONObject)) + default: + return 0, fmt.Errorf("$date value should be string or object, but instead is %s", ejv.t) + } +} + +func parseDatetimeString(data string) (int64, error) { + var t time.Time + var err error + // try acceptable time formats until one matches + for _, format := range timeFormats { + t, err = time.Parse(format, data) + if err == nil { + break + } + } + if err != nil { + return 0, fmt.Errorf("invalid $date value string: %s", data) + } + + return int64(primitive.NewDateTimeFromTime(t)), nil +} + +func parseDatetimeObject(data *extJSONObject) (d int64, err error) { + dFound := false + + for i, key := range data.keys { + val := data.values[i] + + switch key { + case "$numberLong": + if dFound { + return 0, errors.New("duplicate $numberLong key in $date") + } + + if val.t != bsontype.String { + return 0, fmt.Errorf("$date $numberLong field should be string, but instead is %s", val.t) + } + + d, err = val.parseInt64() + if err != nil { + return 0, err + } + dFound = true + default: + return 0, fmt.Errorf("invalid key in $date object: %s", key) + } + } + + if !dFound { + return 0, errors.New("missing $numberLong field in $date object") + } + + return d, nil +} + +func (ejv *extJSONValue) parseDecimal128() (primitive.Decimal128, error) { + if ejv.t != bsontype.String { + return primitive.Decimal128{}, fmt.Errorf("$numberDecimal value should be string, but instead is %s", ejv.t) + } + + d, err := primitive.ParseDecimal128(ejv.v.(string)) + if err != nil { + return primitive.Decimal128{}, fmt.Errorf("$invalid $numberDecimal string: %s", ejv.v.(string)) + } + + return d, nil +} + +func (ejv *extJSONValue) parseDouble() (float64, error) { + if ejv.t == bsontype.Double { + return ejv.v.(float64), nil + } + + if ejv.t != bsontype.String { + return 0, fmt.Errorf("$numberDouble value should be string, but instead is %s", ejv.t) + } + + switch ejv.v.(string) { + case "Infinity": + return math.Inf(1), nil + case "-Infinity": + return math.Inf(-1), nil + case "NaN": + return math.NaN(), nil + } + + f, err := strconv.ParseFloat(ejv.v.(string), 64) + if err != nil { + return 0, err + } + + return f, nil +} + +func (ejv *extJSONValue) parseInt32() (int32, error) { + if ejv.t == bsontype.Int32 { + return ejv.v.(int32), nil + } + + if ejv.t != bsontype.String { + return 0, fmt.Errorf("$numberInt value should be string, but instead is %s", ejv.t) + } + + i, err := strconv.ParseInt(ejv.v.(string), 10, 64) + if err != nil { + return 0, err + } + + if i < math.MinInt32 || i > math.MaxInt32 { + return 0, fmt.Errorf("$numberInt value should be int32 but instead is int64: %d", i) + } + + return int32(i), nil +} + +func (ejv *extJSONValue) parseInt64() (int64, error) { + if ejv.t == bsontype.Int64 { + return ejv.v.(int64), nil + } + + if ejv.t != bsontype.String { + return 0, fmt.Errorf("$numberLong value should be string, but instead is %s", ejv.t) + } + + i, err := strconv.ParseInt(ejv.v.(string), 10, 64) + if err != nil { + return 0, err + } + + return i, nil +} + +func (ejv *extJSONValue) parseJavascript() (code string, err error) { + if ejv.t != bsontype.String { + return "", fmt.Errorf("$code value should be string, but instead is %s", ejv.t) + } + + return ejv.v.(string), nil +} + +func (ejv *extJSONValue) parseMinMaxKey(minmax string) error { + if ejv.t != bsontype.Int32 { + return fmt.Errorf("$%sKey value should be int32, but instead is %s", minmax, ejv.t) + } + + if ejv.v.(int32) != 1 { + return fmt.Errorf("$%sKey value must be 1, but instead is %d", minmax, ejv.v.(int32)) + } + + return nil +} + +func (ejv *extJSONValue) parseObjectID() (primitive.ObjectID, error) { + if ejv.t != bsontype.String { + return primitive.NilObjectID, fmt.Errorf("$oid value should be string, but instead is %s", ejv.t) + } + + return primitive.ObjectIDFromHex(ejv.v.(string)) +} + +func (ejv *extJSONValue) parseRegex() (pattern, options string, err error) { + if ejv.t != bsontype.EmbeddedDocument { + return "", "", fmt.Errorf("$regularExpression value should be object, but instead is %s", ejv.t) + } + + regexObj := ejv.v.(*extJSONObject) + patFound := false + optFound := false + + for i, key := range regexObj.keys { + val := regexObj.values[i] + + switch key { + case "pattern": + if patFound { + return "", "", errors.New("duplicate pattern key in $regularExpression") + } + + if val.t != bsontype.String { + return "", "", fmt.Errorf("$regularExpression pattern value should be string, but instead is %s", val.t) + } + + pattern = val.v.(string) + patFound = true + case "options": + if optFound { + return "", "", errors.New("duplicate options key in $regularExpression") + } + + if val.t != bsontype.String { + return "", "", fmt.Errorf("$regularExpression options value should be string, but instead is %s", val.t) + } + + options = val.v.(string) + optFound = true + default: + return "", "", fmt.Errorf("invalid key in $regularExpression object: %s", key) + } + } + + if !patFound { + return "", "", errors.New("missing pattern field in $regularExpression object") + } + + if !optFound { + return "", "", errors.New("missing options field in $regularExpression object") + + } + + return pattern, options, nil +} + +func (ejv *extJSONValue) parseSymbol() (string, error) { + if ejv.t != bsontype.String { + return "", fmt.Errorf("$symbol value should be string, but instead is %s", ejv.t) + } + + return ejv.v.(string), nil +} + +func (ejv *extJSONValue) parseTimestamp() (t, i uint32, err error) { + if ejv.t != bsontype.EmbeddedDocument { + return 0, 0, fmt.Errorf("$timestamp value should be object, but instead is %s", ejv.t) + } + + handleKey := func(key string, val *extJSONValue, flag bool) (uint32, error) { + if flag { + return 0, fmt.Errorf("duplicate %s key in $timestamp", key) + } + + switch val.t { + case bsontype.Int32: + value := val.v.(int32) + + if value < 0 { + return 0, fmt.Errorf("$timestamp %s number should be uint32: %d", key, value) + } + + return uint32(value), nil + case bsontype.Int64: + value := val.v.(int64) + if value < 0 || value > int64(math.MaxUint32) { + return 0, fmt.Errorf("$timestamp %s number should be uint32: %d", key, value) + } + + return uint32(value), nil + default: + return 0, fmt.Errorf("$timestamp %s value should be uint32, but instead is %s", key, val.t) + } + } + + tsObj := ejv.v.(*extJSONObject) + tFound := false + iFound := false + + for j, key := range tsObj.keys { + val := tsObj.values[j] + + switch key { + case "t": + if t, err = handleKey(key, val, tFound); err != nil { + return 0, 0, err + } + + tFound = true + case "i": + if i, err = handleKey(key, val, iFound); err != nil { + return 0, 0, err + } + + iFound = true + default: + return 0, 0, fmt.Errorf("invalid key in $timestamp object: %s", key) + } + } + + if !tFound { + return 0, 0, errors.New("missing t field in $timestamp object") + } + + if !iFound { + return 0, 0, errors.New("missing i field in $timestamp object") + } + + return t, i, nil +} + +func (ejv *extJSONValue) parseUndefined() error { + if ejv.t != bsontype.Boolean { + return fmt.Errorf("undefined value should be boolean, but instead is %s", ejv.t) + } + + if !ejv.v.(bool) { + return fmt.Errorf("$undefined balue boolean should be true, but instead is %v", ejv.v.(bool)) + } + + return nil +} |