| // 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 bsoncore |
| |
| import ( |
| "bytes" |
| "fmt" |
| |
| "github.com/mongodb/mongo-go-driver/bson/bsontype" |
| ) |
| |
| // MalformedElementError represents a class of errors that RawElement methods return. |
| type MalformedElementError string |
| |
| func (mee MalformedElementError) Error() string { return string(mee) } |
| |
| // ErrElementMissingKey is returned when a RawElement is missing a key. |
| const ErrElementMissingKey MalformedElementError = "element is missing key" |
| |
| // ErrElementMissingType is returned when a RawElement is missing a type. |
| const ErrElementMissingType MalformedElementError = "element is missing type" |
| |
| // Element is a raw bytes representation of a BSON element. |
| type Element []byte |
| |
| // Key returns the key for this element. If the element is not valid, this method returns an empty |
| // string. If knowing if the element is valid is important, use KeyErr. |
| func (e Element) Key() string { |
| key, _ := e.KeyErr() |
| return key |
| } |
| |
| // KeyBytes returns the key for this element as a []byte. If the element is not valid, this method |
| // returns an empty string. If knowing if the element is valid is important, use KeyErr. This method |
| // will not include the null byte at the end of the key in the slice of bytes. |
| func (e Element) KeyBytes() []byte { |
| key, _ := e.KeyBytesErr() |
| return key |
| } |
| |
| // KeyErr returns the key for this element, returning an error if the element is not valid. |
| func (e Element) KeyErr() (string, error) { |
| key, err := e.KeyBytesErr() |
| return string(key), err |
| } |
| |
| // KeyBytesErr returns the key for this element as a []byte, returning an error if the element is |
| // not valid. |
| func (e Element) KeyBytesErr() ([]byte, error) { |
| if len(e) <= 0 { |
| return nil, ErrElementMissingType |
| } |
| idx := bytes.IndexByte(e[1:], 0x00) |
| if idx == -1 { |
| return nil, ErrElementMissingKey |
| } |
| return e[1 : idx+1], nil |
| } |
| |
| // Validate ensures the element is a valid BSON element. |
| func (e Element) Validate() error { |
| if len(e) < 1 { |
| return ErrElementMissingType |
| } |
| idx := bytes.IndexByte(e[1:], 0x00) |
| if idx == -1 { |
| return ErrElementMissingKey |
| } |
| return Value{Type: bsontype.Type(e[0]), Data: e[idx+2:]}.Validate() |
| } |
| |
| // CompareKey will compare this element's key to key. This method makes it easy to compare keys |
| // without needing to allocate a string. The key may be null terminated. If a valid key cannot be |
| // read this method will return false. |
| func (e Element) CompareKey(key []byte) bool { |
| if len(e) < 2 { |
| return false |
| } |
| idx := bytes.IndexByte(e[1:], 0x00) |
| if idx == -1 { |
| return false |
| } |
| if index := bytes.IndexByte(key, 0x00); index > -1 { |
| key = key[:index] |
| } |
| return bytes.Equal(e[1:idx+1], key) |
| } |
| |
| // Value returns the value of this element. If the element is not valid, this method returns an |
| // empty Value. If knowing if the element is valid is important, use ValueErr. |
| func (e Element) Value() Value { |
| val, _ := e.ValueErr() |
| return val |
| } |
| |
| // ValueErr returns the value for this element, returning an error if the element is not valid. |
| func (e Element) ValueErr() (Value, error) { |
| if len(e) <= 0 { |
| return Value{}, ErrElementMissingType |
| } |
| idx := bytes.IndexByte(e[1:], 0x00) |
| if idx == -1 { |
| return Value{}, ErrElementMissingKey |
| } |
| |
| val, rem, exists := ReadValue(e[idx+2:], bsontype.Type(e[0])) |
| if !exists { |
| return Value{}, NewInsufficientBytesError(e, rem) |
| } |
| return val, nil |
| } |
| |
| // String implements the fmt.String interface. The output will be in extended JSON format. |
| func (e Element) String() string { |
| if len(e) <= 0 { |
| return "" |
| } |
| t := bsontype.Type(e[0]) |
| idx := bytes.IndexByte(e[1:], 0x00) |
| if idx == -1 { |
| return "" |
| } |
| key, valBytes := []byte(e[1:idx+1]), []byte(e[idx+2:]) |
| val, _, valid := ReadValue(valBytes, t) |
| if !valid { |
| return "" |
| } |
| return fmt.Sprintf(`"%s": %v`, key, val) |
| } |
| |
| // DebugString outputs a human readable version of RawElement. It will attempt to stringify the |
| // valid components of the element even if the entire element is not valid. |
| func (e Element) DebugString() string { |
| if len(e) <= 0 { |
| return "<malformed>" |
| } |
| t := bsontype.Type(e[0]) |
| idx := bytes.IndexByte(e[1:], 0x00) |
| if idx == -1 { |
| return fmt.Sprintf(`bson.Element{[%s]<malformed>}`, t) |
| } |
| key, valBytes := []byte(e[1:idx+1]), []byte(e[idx+2:]) |
| val, _, valid := ReadValue(valBytes, t) |
| if !valid { |
| return fmt.Sprintf(`bson.Element{[%s]"%s": <malformed>}`, t, key) |
| } |
| return fmt.Sprintf(`bson.Element{[%s]"%s": %v}`, t, key, val) |
| } |