rawbson 0.2.1

Blazing fast zero-copy BSON handling
Documentation
/*!
A rawbson document can be created from a `Vec<u8>` containing raw BSON data, and elements
accessed via methods similar to those in the [bson-rust](https://crates.io/crate/bson-rust)
crate.  Note that rawbson returns a Result<Option<T>>, since the bytes contained in the
document are not fully validated until trying to access the contained data.

```rust
use rawbson::{
    DocBuf,
    elem,
};

// \x13\x00\x00\x00           // total document size
// \x02                       // 0x02 = type String
// hi\x00                     // field name
// \x06\x00\x00\x00y'all\x00  // field value
// \x00                       // document terminating NUL

let doc = DocBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?;
let elem: Option<elem::Element> = doc.get("hi")?;
assert_eq!(
    elem.unwrap().as_str()?,
    "y'all",
);
# Ok::<(), rawbson::RawError>(())
```

### bson-rust interop

This crate is designed to interoperate smoothly with the bson crate.

A [`DocBuf`] can be created from a [`bson::document::Document`].  Internally, this
serializes the `Document` to a `Vec<u8>`, and then includes those bytes in the [`DocBuf`].

```rust
use bson::doc;
use rawbson::{
    DocBuf,
};

let document = doc!{"goodbye": {"cruel": "world"}};
let raw = DocBuf::from_document(&document);
let value: Option<&str> = raw.get_document("goodbye")?
    .map(|doc| doc.get_str("cruel"))
    .transpose()?
    .flatten();

assert_eq!(
    value,
    Some("world"),
);
# Ok::<(), rawbson::RawError>(())
```

### Reference types

A BSON document can also be accessed with the [`Doc`] reference type,
which is an unsized type that represents the BSON payload as a `[u8]`.
This allows accessing nested documents without reallocation.  [Doc]
must always be accessed via a pointer type, similarly to `[T]` and `str`.

This type will coexist with the now deprecated [DocRef] type for at
least one minor release.

The below example constructs a bson document in a stack-based array,
and extracts a &str from it, performing no heap allocation.

```rust
use rawbson::Doc;

let bytes = b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00";
assert_eq!(Doc::new(bytes)?.get_str("hi")?, Some("y'all"));
# Ok::<(), rawbson::RawError>(())
```

### Iteration

[`Doc`] implements [`IntoIterator`](std::iter::IntoIterator), which can also
be accessed via [`DocBuf::iter`].

```rust
use bson::doc;
use rawbson::{DocBuf, elem::Element};

let doc = DocBuf::from_document(&doc! {"crate": "rawbson", "license": "MIT"});
let mut dociter = doc.iter();

let (key, value): (&str, Element) = dociter.next().unwrap()?;
assert_eq!(key, "crate");
assert_eq!(value.as_str()?, "rawbson");

let (key, value): (&str, Element) = dociter.next().unwrap()?;
assert_eq!(key, "license");
assert_eq!(value.as_str()?, "MIT");
# Ok::<(), rawbson::RawError>(())
```

### serde support

There is also serde deserialization support.

Serde serialization support is not yet provided.  For now, use
[`bson::to_document`] instead, and then serialize it out using
[`bson::Document::to_writer`] or [`DocBuf::from_document`].

```rust
use serde::Deserialize;
use bson::{doc, Document, oid::ObjectId, DateTime};
use rawbson::{DocBuf, de::from_docbuf};

#[derive(Deserialize)]
#[serde(rename_all="camelCase")]
struct User {
    #[serde(rename = "_id")]
    id: ObjectId,
    first_name: String,
    last_name: String,
    birthdate: Option<chrono::DateTime<chrono::Utc>>,
    #[serde(flatten)]
    extra: Document,
}

let doc = DocBuf::from_document(&doc!{
    "_id": ObjectId::with_string("543254325432543254325432")?,
    "firstName": "John",
    "lastName": "Doe",
    "birthdate": null,
    "luckyNumbers": [3, 60, 2147483647],
    "nickname": "Red",
});

let user: User = from_docbuf(&doc)?;
assert_eq!(user.id.to_hex(), "543254325432543254325432");
assert_eq!(user.first_name, "John");
assert_eq!(user.last_name, "Doe");
assert_eq!(user.extra.get_str("nickname")?, "Red");
assert!(user.birthdate.is_none());
# Ok::<(), Box<dyn std::error::Error>>(())
```
*/

use std::{
    borrow::Borrow,
    convert::{TryFrom, TryInto},
    ops::Deref,
};

use chrono::{DateTime, Utc};

use bson::{decimal128::Decimal128, document::ValueAccessError, oid, spec::ElementType, Bson};

pub mod de;
pub mod elem;

#[cfg(test)]
mod props;

/// Error to indicate that either a value was empty or it contained an unexpected
/// type, for use with the direct getters.
#[derive(Debug, PartialEq)]
pub enum RawError {
    /// Found a Bson value with the specified key, but not with the expected type
    UnexpectedType,

    /// The found value was not well-formed
    MalformedValue(String),

    /// Found a value where a utf-8 string was expected, but it was not valid
    /// utf-8.  The error value contains the malformed data as a string.
    Utf8EncodingError(Vec<u8>),
}

impl std::fmt::Display for RawError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use RawError::*;
        match self {
            UnexpectedType => write!(f, "unexpected type"),
            MalformedValue(s) => write!(f, "malformed value: {:?}", s),
            Utf8EncodingError(_) => write!(f, "utf-8 encoding error"),
        }
    }
}

impl std::error::Error for RawError {}

pub type RawResult<T> = Result<T, RawError>;
type OptResult<T> = RawResult<Option<T>>;

impl<'a> From<RawError> for ValueAccessError {
    fn from(src: RawError) -> ValueAccessError {
        match src {
            RawError::UnexpectedType => ValueAccessError::UnexpectedType,
            RawError::MalformedValue(_) => ValueAccessError::UnexpectedType,
            RawError::Utf8EncodingError(_) => ValueAccessError::UnexpectedType,
        }
    }
}

impl<'a> From<ValueAccessError> for RawError {
    fn from(src: ValueAccessError) -> RawError {
        match src {
            ValueAccessError::NotPresent => unreachable!("This should be converted to an Option"),
            ValueAccessError::UnexpectedType => RawError::UnexpectedType,
            _ => RawError::UnexpectedType,
        }
    }
}

/// A BSON document, stored as raw binary data on the heap.  This can be created from
/// a `Vec<u8>` or a [`bson::Document`].
///
/// Accessing elements within the `DocBuf` is similar to element access in [bson::Document],
/// but as the contents are parsed during iteration, instead of at creation time, format
/// errors can happen at any time during use, instead of at creation time.
///
/// DocBuf can be iterated over, yielding a Result containing key-value pairs that
/// borrow from the DocBuf instead of allocating, when necessary.
///
/// ```
/// # use rawbson::{DocBuf, RawError};
/// let docbuf = DocBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?;
/// let mut iter = docbuf.iter();
/// let (key, value) = iter.next().unwrap()?;
/// assert_eq!(key, "hi");
/// assert_eq!(value.as_str(), Ok("y'all"));
/// assert!(iter.next().is_none());
/// # Ok::<(), RawError>(())
/// ```
///
/// Individual elements can be accessed using [`docbuf.get(&key)`](Doc::get), or any of
/// the `get_*` methods, like [`docbuf.get_object_id(&key)`](Doc::get_object_id), and
/// [`docbuf.get_str(&str)`](Doc::get_str).  Accessing elements is an O(N) operation,
/// as it requires iterating through the document from the beginning to find the requested
/// key.
///
/// ```
/// # use rawbson::{DocBuf, RawError};
/// let docbuf = DocBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?;
/// assert_eq!(docbuf.get_str("hi")?, Some("y'all"));
/// # Ok::<(), RawError>(())
/// ```
#[derive(Clone, Debug)]
pub struct DocBuf {
    data: Box<[u8]>,
}

impl DocBuf {
    /// Create a new `DocBuf` from the provided `Vec`.
    ///
    /// The data is checked for a declared length equal to the length of the Vec,
    /// and a trailing NUL byte.  Other validation is deferred to access time.
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// let docbuf: DocBuf = DocBuf::new(b"\x05\0\0\0\0".to_vec())?;
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn new(data: Vec<u8>) -> RawResult<DocBuf> {
        if data.len() < 5 {
            return Err(RawError::MalformedValue("document too short".into()));
        }
        let length = i32_from_slice(&data[..4]);
        if data.len() as i32 != length {
            return Err(RawError::MalformedValue("document length incorrect".into()));
        }
        if data[data.len() - 1] != 0 {
            return Err(RawError::MalformedValue(
                "document not null-terminated".into(),
            ));
        }
        Ok(unsafe { DocBuf::new_unchecked(data) })
    }

    /// Create a DocBuf from a [bson::Document].
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// use bson::{doc, oid};
    /// let document = doc! {
    ///     "_id": oid::ObjectId::new(),
    ///     "name": "Herman Melville",
    ///     "title": "Moby-Dick",
    /// };
    /// let docbuf: DocBuf = DocBuf::from_document(&document);
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn from_document(doc: &bson::Document) -> DocBuf {
        let mut data = Vec::new();
        doc.to_writer(&mut data).unwrap();
        unsafe { DocBuf::new_unchecked(data) }
    }

    /// Create a DocBuf from an owned Vec<u8> without performing any checks on the provided data.
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// let docbuf: DocBuf = unsafe {
    ///     DocBuf::new_unchecked(b"\x05\0\0\0\0".to_vec())
    /// };
    /// # Ok::<(), RawError>(())
    /// ```
    ///
    /// # Safety
    ///
    /// The provided bytes must have a valid length marker, and be NUL terminated.
    pub unsafe fn new_unchecked(data: Vec<u8>) -> DocBuf {
        DocBuf {
            data: data.into_boxed_slice(),
        }
    }

    /// Return a [`&Doc`](Doc) borrowing from the data contained in self.
    ///
    /// # Deprecation
    ///
    /// DocRef is now a deprecated type alias for [Doc].  DocBuf can
    /// dereference to &Doc directly, or be converted using [AsRef::as_ref],
    /// so this function is unnecessary.
    ///
    /// ```
    /// # use rawbson::{DocBuf, DocRef, RawError};
    /// let docbuf = DocBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?;
    /// let docref: DocRef = docbuf.as_docref();
    /// # Ok::<(), RawError>(())
    /// ```
    #[deprecated(since = "0.2.0", note = "use docbuf.as_ref() instead")]
    pub fn as_docref(&self) -> &Doc {
        self.as_ref()
    }

    /// Return an iterator over the elements in the `DocBuf`, borrowing data.
    ///
    /// The associated item type is `Result<&str, Element<'_>>`.  An error is
    /// returned if data is malformed.
    ///
    /// ```
    /// # use rawbson::{elem, DocBuf, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! { "ferris": true });
    /// for element in docbuf.iter() {
    ///     let (key, value): (&str, elem::Element) = element?;
    ///     assert_eq!(key, "ferris");
    ///     assert_eq!(value.as_bool()?, true);
    /// }
    /// # Ok::<(), RawError>(())
    /// ```
    ///
    /// # Note:
    ///
    /// There is no owning iterator for DocBuf.  If you need ownership over
    /// elements that might need to allocate, you must explicitly convert
    /// them to owned types yourself.
    pub fn iter(&self) -> DocIter<'_> {
        self.into_iter()
    }

    /// Return the contained data as a `Vec<u8>`
    ///
    /// ```
    /// # use rawbson::DocBuf;
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc!{});
    /// assert_eq!(docbuf.into_inner(), b"\x05\x00\x00\x00\x00".to_vec());
    /// ```
    pub fn into_inner(self) -> Vec<u8> {
        self.data.to_vec()
    }
}

impl TryFrom<DocBuf> for bson::Document {
    type Error = RawError;

    fn try_from(rawdoc: DocBuf) -> RawResult<bson::Document> {
        bson::Document::try_from(rawdoc.as_ref())
    }
}

impl<'a> IntoIterator for &'a DocBuf {
    type IntoIter = DocIter<'a>;
    type Item = RawResult<(&'a str, elem::Element<'a>)>;

    fn into_iter(self) -> DocIter<'a> {
        DocIter {
            doc: &self,
            offset: 4,
        }
    }
}

impl AsRef<Doc> for DocBuf {
    fn as_ref(&self) -> &Doc {
        // SAFETY: Constructing the DocBuf checks the envelope validity of the BSON document.
        unsafe { Doc::new_unchecked(&self.data) }
    }
}

impl Borrow<Doc> for DocBuf {
    fn borrow(&self) -> &Doc {
        &*self
    }
}

impl ToOwned for Doc {
    type Owned = DocBuf;

    fn to_owned(&self) -> Self::Owned {
        self.to_docbuf()
    }
}

/// A BSON document, referencing raw binary data stored elsewhere.  This can be created from
/// a [DocBuf] or any type that contains valid BSON data, and can be referenced as a `[u8]`,
/// including static binary literals, [Vec<u8>](std::vec::Vec), or arrays.
///
/// Accessing elements within the `Doc` is similar to element access in [bson::Document],
/// but as the contents are parsed during iteration, instead of at creation time, format
/// errors can happen at any time during use, instead of at creation time.
///
/// Doc can be iterated over, yielding a Result containing key-value pairs that share the
/// borrow with the source bytes instead of allocating, when necessary.
///
/// ```
/// # use rawbson::{Doc, RawError};
/// let doc = Doc::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00")?;
/// let mut iter = doc.into_iter();
/// let (key, value) = iter.next().unwrap()?;
/// assert_eq!(key, "hi");
/// assert_eq!(value.as_str(), Ok("y'all"));
/// assert!(iter.next().is_none());
/// # Ok::<(), RawError>(())
/// ```
///
/// Individual elements can be accessed using [`doc.get(&key)`](Doc::get), or any of
/// the `get_*` methods, like [`doc.get_object_id(&key)`](Doc::get_object_id), and
/// [`doc.get_str(&str)`](Doc::get_str).  Accessing elements is an O(N) operation,
/// as it requires iterating through the document from the beginning to find the requested
/// key.
///
/// ```
/// # use rawbson::{DocBuf, RawError};
/// let docbuf = DocBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?;
/// assert_eq!(docbuf.get_str("hi")?, Some("y'all"));
/// # Ok::<(), RawError>(())
/// ```
#[derive(Debug)]
pub struct Doc {
    data: [u8],
}

impl Doc {
    pub fn new<D: AsRef<[u8]> + ?Sized>(data: &D) -> RawResult<&Doc> {
        let data = data.as_ref();
        if data.len() < 5 {
            return Err(RawError::MalformedValue("document too short".into()));
        }
        let length = i32_from_slice(&data[..4]);
        if data.len() as i32 != length {
            return Err(RawError::MalformedValue("document length incorrect".into()));
        }
        if data[data.len() - 1] != 0 {
            return Err(RawError::MalformedValue(
                "document not null-terminated".into(),
            ));
        }
        Ok(unsafe { Doc::new_unchecked(data) })
    }

    /// Create a new Doc referencing the provided data slice.
    ///
    /// # Safety
    ///
    /// The provided data must begin with a valid size
    /// and end with a NUL-terminator.
    ///
    /// ```
    /// # use rawbson::{Doc, RawError};
    /// let doc: &Doc = unsafe { Doc::new_unchecked(b"\x05\0\0\0\0") };
    /// ```
    pub unsafe fn new_unchecked<D: AsRef<[u8]> + ?Sized>(data: &D) -> &Doc {
        #[allow(unused_unsafe)]
        unsafe {
            &*(data.as_ref() as *const [u8] as *const Doc)
        }
    }

    /// Create a new DocBuf with an owned copy of the data in self.
    ///
    /// ```
    /// # use rawbson::{Doc, RawError};
    /// use rawbson::DocBuf;
    /// let data = b"\x05\0\0\0\0";
    /// let doc = Doc::new(data)?;
    /// let docbuf: DocBuf = doc.to_docbuf();
    /// # Ok::<(), RawError>(())
    pub fn to_docbuf(&self) -> DocBuf {
        // SAFETY: The validity of the data is checked by self.
        unsafe { DocBuf::new_unchecked(self.data.to_owned()) }
    }

    /// Get an element from the document.  Finding a particular key requires
    /// iterating over the document from the beginning, so this is an O(N)
    /// operation.
    ///
    /// Returns an error if the document is malformed.  Returns `Ok(None)`
    /// if the key is not found in the document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem::Element, RawError};
    /// use bson::{doc, oid::ObjectId};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "_id": ObjectId::new(),
    ///     "f64": 2.5,
    /// });
    /// let element = docbuf.get("f64")?.expect("finding key f64");
    /// assert_eq!(element.as_f64(), Ok(2.5));
    /// assert!(docbuf.get("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get<'a>(&'a self, key: &str) -> OptResult<elem::Element<'a>> {
        for result in self.into_iter() {
            let (thiskey, bson) = result?;
            if thiskey == key {
                return Ok(Some(bson));
            }
        }
        Ok(None)
    }

    fn get_with<'a, T>(
        &'a self,
        key: &str,
        f: impl FnOnce(elem::Element<'a>) -> RawResult<T>,
    ) -> OptResult<T> {
        self.get(key)?.map(f).transpose()
    }

    /// Get an element from the document, and convert it to f64.
    ///
    /// Returns an error if the document is malformed, or if the retrieved value
    /// is not an f64.  Returns `Ok(None)` if the key is not found in the document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem::Element, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "bool": true,
    ///     "f64": 2.5,
    /// });
    /// assert_eq!(docbuf.get_f64("f64"), Ok(Some(2.5)));
    /// assert_eq!(docbuf.get_f64("bool"), Err(RawError::UnexpectedType));
    /// assert_eq!(docbuf.get_f64("unknown"), Ok(None));
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_f64(&self, key: &str) -> OptResult<f64> {
        self.get_with(key, elem::Element::as_f64)
    }

    /// Get an element from the document, and convert it to a &str.
    ///
    /// The returned &str is a borrowed reference into the DocBuf.  To use it
    /// beyond the lifetime of self, call to_docbuf() on it.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a string.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem::Element, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "string": "hello",
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_str("string"), Ok(Some("hello")));
    /// assert_eq!(docbuf.get_str("bool"), Err(RawError::UnexpectedType));
    /// assert_eq!(docbuf.get_str("unknown"), Ok(None));
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_str<'a>(&'a self, key: &str) -> OptResult<&'a str> {
        self.get_with(key, elem::Element::as_str)
    }

    /// Get an element from the document, and convert it to a [Doc].
    ///
    /// The returned [Doc] is a borrowed reference into self.  To use it
    /// beyond the lifetime of self, call to_owned() on it.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a document.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem::Element, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "doc": { "key": "value"},
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_document("doc")?.expect("finding key doc").get_str("key"), Ok(Some("value")));
    /// assert_eq!(docbuf.get_document("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_document("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_document<'a>(&'a self, key: &str) -> OptResult<&'a Doc> {
        self.get_with(key, elem::Element::as_document)
    }

    /// Get an element from the document, and convert it to an [ArrayRef].
    ///
    /// The returned [ArrayRef] is a borrowed reference into the DocBuf.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a document.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem::Element, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "array": [true, 3, null],
    ///     "bool": true,
    /// });
    /// let mut arriter = docbuf.get_array("array")?.expect("finding key array").into_iter();
    /// let _: bool = arriter.next().unwrap()?.as_bool()?;
    /// let _: i32 = arriter.next().unwrap()?.as_i32()?;
    /// let () = arriter.next().unwrap()?.as_null()?;
    /// assert!(arriter.next().is_none());
    /// assert!(docbuf.get_array("bool").is_err());
    /// assert!(docbuf.get_array("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_array<'a>(&'a self, key: &str) -> OptResult<&'a Array> {
        self.get_with(key, elem::Element::as_array)
    }

    /// Get an element from the document, and convert it to an [elem::RawBsonBinary].
    ///
    /// The returned [RawBsonBinary](elem::RawBsonBinary) is a borrowed reference into the DocBuf.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not binary data.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem, RawError};
    /// use bson::{doc, Binary, spec::BinarySubtype};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1, 2, 3] },
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_binary("binary")?.map(elem::RawBsonBinary::as_bytes), Some(&[1, 2, 3][..]));
    /// assert_eq!(docbuf.get_binary("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_binary("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_binary<'a>(&'a self, key: &str) -> OptResult<elem::RawBsonBinary<'a>> {
        self.get_with(key, elem::Element::as_binary)
    }

    /// Get an element from the document, and convert it to a [bson::oid::ObjectId].
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not an object ID.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// use bson::{doc, oid::ObjectId};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "_id": ObjectId::new(),
    ///     "bool": true,
    /// });
    /// let _: ObjectId = docbuf.get_object_id("_id")?.unwrap();
    /// assert_eq!(docbuf.get_object_id("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_object_id("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_object_id(&self, key: &str) -> OptResult<oid::ObjectId> {
        self.get_with(key, elem::Element::as_object_id)
    }

    /// Get an element from the document, and convert it to a [bool].
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a boolean.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// use bson::{doc, oid::ObjectId};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "_id": ObjectId::new(),
    ///     "bool": true,
    /// });
    /// assert!(docbuf.get_bool("bool")?.unwrap());
    /// assert_eq!(docbuf.get_bool("_id").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_object_id("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_bool(&self, key: &str) -> OptResult<bool> {
        self.get_with(key, elem::Element::as_bool)
    }

    /// Get an element from the document, and convert it to a [chrono::DateTime].
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a boolean.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// use bson::doc;
    /// use chrono::{Utc, Datelike, TimeZone};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "created_at": Utc.ymd(2020, 3, 15).and_hms(17, 0, 0),
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_datetime("created_at")?.unwrap().year(), 2020);
    /// assert_eq!(docbuf.get_datetime("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_datetime("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_datetime(&self, key: &str) -> OptResult<DateTime<Utc>> {
        self.get_with(key, elem::Element::as_datetime)
    }

    /// Get an element from the document, and convert it to the `()` type.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not null.  Returns `Ok(None)` if the key is not found in the
    /// document.
    ///
    /// There is not much reason to use the () value, so this method mostly
    /// exists for consistency with other element types, and as a way to assert
    /// type of the element.
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "null": null,
    ///     "bool": true,
    /// });
    /// docbuf.get_null("null")?.unwrap();
    /// assert_eq!(docbuf.get_null("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_null("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_null(&self, key: &str) -> OptResult<()> {
        self.get_with(key, elem::Element::as_null)
    }

    /// Get an element from the document, and convert it to an [elem::RawBsonRegex].
    ///
    /// The [RawBsonRegex](elem::RawBsonRegex) borrows data from the DocBuf.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a regex.  Returns `Ok(None)` if the key is not found in the
    /// document.
    /// ```
    /// # use rawbson::{DocBuf, RawError, elem};
    /// use bson::{doc, Regex};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "regex": Regex {
    ///         pattern: String::from(r"end\s*$"),
    ///         options: String::from("i"),
    ///     },
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_regex("regex")?.unwrap().pattern(), r"end\s*$");
    /// assert_eq!(docbuf.get_regex("regex")?.unwrap().options(), "i");
    /// assert_eq!(docbuf.get_regex("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_regex("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_regex<'a>(&'a self, key: &str) -> OptResult<elem::RawBsonRegex<'a>> {
        self.get_with(key, elem::Element::as_regex)
    }

    /// Get an element from the document, and convert it to an &str representing the
    /// javascript element type.
    ///
    /// The &str borrows data from the DocBuf.  If you need an owned copy of the data,
    /// you should call .to_owned() on the result.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a javascript code object.  Returns `Ok(None)` if the key is not found
    /// in the document.
    /// ```
    /// # use rawbson::{DocBuf, RawError, elem};
    /// use bson::{doc, Bson};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "js": Bson::JavaScriptCode(String::from("console.log(\"hi y'all\");")),
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_javascript("js")?, Some("console.log(\"hi y'all\");"));
    /// assert_eq!(docbuf.get_javascript("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_javascript("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_javascript<'a>(&'a self, key: &str) -> OptResult<&'a str> {
        self.get_with(key, elem::Element::as_javascript)
    }

    /// Get an element from the document, and convert it to an &str representing the
    /// symbol element type.
    ///
    /// The &str borrows data from the DocBuf.  If you need an owned copy of the data,
    /// you should call .to_owned() on the result.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a symbol object.  Returns `Ok(None)` if the key is not found
    /// in the document.
    /// ```
    /// # use rawbson::{DocBuf, RawError, elem};
    /// use bson::{doc, Bson};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "symbol": Bson::Symbol(String::from("internal")),
    ///     "bool": true,
    /// });
    /// assert_eq!(docbuf.get_symbol("symbol")?, Some("internal"));
    /// assert_eq!(docbuf.get_symbol("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_symbol("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_symbol<'a>(&'a self, key: &str) -> OptResult<&'a str> {
        self.get_with(key, elem::Element::as_symbol)
    }

    /// Get an element from the document, and extract the data as a javascript code with scope.
    ///
    /// The return value is a `(&str, &Doc)` where the &str represents the javascript code,
    /// and the [`&Doc`](Doc) represents the scope.  Both elements borrow data from the DocBuf.
    /// If you need an owned copy of the data, you should call [js.to_owned()](ToOwned::to_owned) on
    /// the code or [scope.to_docbuf()](Doc::to_docbuf) on the scope.
    ///
    /// Returns an error if the document is malformed or if the retrieved value
    /// is not a javascript code with scope object.  Returns `Ok(None)` if the key is not found
    /// in the document.
    /// ```
    /// # use rawbson::{DocBuf, RawError, elem};
    /// use bson::{doc, JavaScriptCodeWithScope};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "js": JavaScriptCodeWithScope {
    ///         code: String::from("console.log(\"i:\", i);"),
    ///         scope: doc!{"i": 42},
    ///     },
    ///     "bool": true,
    /// });
    /// let (js, scope) = docbuf.get_javascript_with_scope("js")?.unwrap();
    /// assert_eq!(js, "console.log(\"i:\", i);");
    /// assert_eq!(scope.get_i32("i")?.unwrap(), 42);
    /// assert_eq!(docbuf.get_javascript_with_scope("bool").unwrap_err(), RawError::UnexpectedType);
    /// assert!(docbuf.get_javascript_with_scope("unknown")?.is_none());
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_javascript_with_scope<'a>(&'a self, key: &str) -> OptResult<(&'a str, &'a Doc)> {
        self.get_with(key, elem::Element::as_javascript_with_scope)
    }

    /// Get an element from the document, and convert it to i32.
    ///
    /// Returns an error if the document is malformed, or if the retrieved value
    /// is not an i32.  Returns `Ok(None)` if the key is not found in the document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "bool": true,
    ///     "i32": 1_000_000,
    /// });
    /// assert_eq!(docbuf.get_i32("i32"), Ok(Some(1_000_000)));
    /// assert_eq!(docbuf.get_i32("bool"), Err(RawError::UnexpectedType));
    /// assert_eq!(docbuf.get_i32("unknown"), Ok(None));
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_i32(&self, key: &str) -> OptResult<i32> {
        self.get_with(key, elem::Element::as_i32)
    }

    /// Get an element from the document, and convert it to a timestamp.
    ///
    /// Returns an error if the document is malformed, or if the retrieved value
    /// is not an i32.  Returns `Ok(None)` if the key is not found in the document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem, RawError};
    /// use bson::{doc, Timestamp};
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "bool": true,
    ///     "ts": Timestamp { time: 649876543, increment: 9 },
    /// });
    /// let timestamp = docbuf.get_timestamp("ts")?.unwrap();
    ///
    /// assert_eq!(timestamp.time(), 649876543);
    /// assert_eq!(timestamp.increment(), 9);
    /// assert_eq!(docbuf.get_timestamp("bool"), Err(RawError::UnexpectedType));
    /// assert_eq!(docbuf.get_timestamp("unknown"), Ok(None));
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_timestamp<'a>(&'a self, key: &str) -> OptResult<elem::RawBsonTimestamp<'a>> {
        self.get_with(key, elem::Element::as_timestamp)
    }

    /// Get an element from the document, and convert it to i64.
    ///
    /// Returns an error if the document is malformed, or if the retrieved value
    /// is not an i64.  Returns `Ok(None)` if the key is not found in the document.
    ///
    /// ```
    /// # use rawbson::{DocBuf, elem::Element, RawError};
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc! {
    ///     "bool": true,
    ///     "i64": 9223372036854775807_i64,
    /// });
    /// assert_eq!(docbuf.get_i64("i64"), Ok(Some(9223372036854775807)));
    /// assert_eq!(docbuf.get_i64("bool"), Err(RawError::UnexpectedType));
    /// assert_eq!(docbuf.get_i64("unknown"), Ok(None));
    /// # Ok::<(), RawError>(())
    /// ```
    pub fn get_i64(&self, key: &str) -> OptResult<i64> {
        self.get_with(key, elem::Element::as_i64)
    }

    /// Return a reference to the contained data as a `&[u8]`
    ///
    /// ```
    /// # use rawbson::DocBuf;
    /// use bson::doc;
    /// let docbuf = DocBuf::from_document(&doc!{});
    /// assert_eq!(docbuf.as_bytes(), b"\x05\x00\x00\x00\x00");
    /// ```
    pub fn as_bytes(&self) -> &[u8] {
        &self.data
    }
}

impl AsRef<Doc> for Doc {
    fn as_ref(&self) -> &Doc {
        self
    }
}

impl Deref for DocBuf {
    type Target = Doc;

    fn deref(&self) -> &Self::Target {
        // SAFETY: The validity of the data is checked when creating DocBuf.
        unsafe { Doc::new_unchecked(&self.data) }
    }
}

impl TryFrom<&Doc> for bson::Document {
    type Error = RawError;

    fn try_from(rawdoc: &Doc) -> RawResult<bson::Document> {
        rawdoc
            .into_iter()
            .map(|res| res.and_then(|(k, v)| Ok((k.to_owned(), v.try_into()?))))
            .collect()
    }
}

impl<'a> IntoIterator for &'a Doc {
    type IntoIter = DocIter<'a>;
    type Item = RawResult<(&'a str, elem::Element<'a>)>;

    fn into_iter(self) -> DocIter<'a> {
        DocIter {
            doc: self,
            offset: 4,
        }
    }
}

pub struct DocIter<'a> {
    doc: &'a Doc,
    offset: usize,
}

impl<'a> Iterator for DocIter<'a> {
    type Item = RawResult<(&'a str, elem::Element<'a>)>;

    fn next(&mut self) -> Option<RawResult<(&'a str, elem::Element<'a>)>> {
        if self.offset == self.doc.data.len() - 1 {
            if self.doc.data[self.offset] == 0 {
                // end of document marker
                return None;
            } else {
                return Some(Err(RawError::MalformedValue(
                    "document not null terminated".into(),
                )));
            }
        }
        let key = match read_nullterminated(&self.doc.data[self.offset + 1..]) {
            Ok(key) => key,
            Err(err) => return Some(Err(err)),
        };
        let valueoffset = self.offset + 1 + key.len() + 1; // type specifier + key + \0
        let element_type = match ElementType::from(self.doc.data[self.offset]) {
            Some(et) => et,
            None => {
                return Some(Err(RawError::MalformedValue(format!(
                    "invalid tag: {}",
                    self.doc.data[self.offset]
                ))))
            }
        };
        let element_size = match element_type {
            ElementType::Double => 8,
            ElementType::String => {
                let size =
                    4 + i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize;
                if self.doc.data[valueoffset + size - 1] != 0 {
                    return Some(Err(RawError::MalformedValue(
                        "string not null terminated".into(),
                    )));
                }
                size
            }
            ElementType::EmbeddedDocument => {
                let size = i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize;
                if self.doc.data[valueoffset + size - 1] != 0 {
                    return Some(Err(RawError::MalformedValue(
                        "document not null terminated".into(),
                    )));
                }
                size
            }
            ElementType::Array => {
                let size = i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize;
                if self.doc.data[valueoffset + size - 1] != 0 {
                    return Some(Err(RawError::MalformedValue(
                        "array not null terminated".into(),
                    )));
                }
                size
            }
            ElementType::Binary => {
                5 + i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize
            }
            ElementType::Undefined => 0,
            ElementType::ObjectId => 12,
            ElementType::Boolean => 1,
            ElementType::DateTime => 8,
            ElementType::Null => 0,
            ElementType::RegularExpression => {
                let regex = match read_nullterminated(&self.doc.data[valueoffset..]) {
                    Ok(regex) => regex,
                    Err(err) => return Some(Err(err)),
                };
                let options =
                    match read_nullterminated(&self.doc.data[valueoffset + regex.len() + 1..]) {
                        Ok(options) => options,
                        Err(err) => return Some(Err(err)),
                    };
                regex.len() + options.len() + 2
            }
            ElementType::DbPointer => {
                let string_size =
                    4 + i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize;
                let id_size = 12;
                if self.doc.data[valueoffset + string_size - 1] != 0 {
                    return Some(Err(RawError::MalformedValue(
                        "DBPointer string not null-terminated".into(),
                    )));
                }
                string_size + id_size
            }
            ElementType::JavaScriptCode => {
                let size =
                    4 + i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize;
                if self.doc.data[valueoffset + size - 1] != 0 {
                    return Some(Err(RawError::MalformedValue(
                        "javascript code not null-terminated".into(),
                    )));
                }
                size
            }
            ElementType::Symbol => {
                4 + i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize
            }
            ElementType::JavaScriptCodeWithScope => {
                let size = i32_from_slice(&self.doc.data[valueoffset..valueoffset + 4]) as usize;
                if self.doc.data[valueoffset + size - 1] != 0 {
                    return Some(Err(RawError::MalformedValue(
                        "javascript with scope not null-terminated".into(),
                    )));
                }
                size
            }
            ElementType::Int32 => 4,
            ElementType::Timestamp => 8,
            ElementType::Int64 => 8,
            ElementType::Decimal128 => 16,
            ElementType::MaxKey => 0,
            ElementType::MinKey => 0,
        };
        let nextoffset = valueoffset + element_size;
        self.offset = nextoffset;
        Some(Ok((
            key,
            elem::Element::new(element_type, &self.doc.data[valueoffset..nextoffset]),
        )))
    }
}

pub type ArrayRef<'a> = &'a Array;

pub struct Array {
    doc: Doc,
}

impl Array {
    pub fn new(data: &[u8]) -> RawResult<&Array> {
        Ok(Array::from_doc(Doc::new(data)?))
    }

    /// Return a new Array from the provided bytes.
    ///
    /// # Safety
    ///
    /// The provided bytes must start with a valid length indicator
    /// and end with a NUL terminator, as described in [the bson
    /// spec](http://bsonspec.org/spec.html).
    ///
    /// The following is valid:
    /// ```
    /// # use rawbson::Array;
    /// // Represents the array [null, 514i32], which is the same as the document
    /// // {"0": null, "1": 514}
    /// let bson = b"\x0f\0\0\0\x0A0\0\x101\0\x02\x02\0\0\0";
    /// let arr = unsafe { Array::new_unchecked(bson) };
    /// let mut arriter = arr.into_iter();
    /// assert!(arriter.next().unwrap().and_then(|b| b.as_null()).is_ok());
    /// assert_eq!(arriter.next().unwrap().and_then(|b| b.as_i32()).unwrap(), 514);
    /// ```
    ///
    /// And so is this, even though the provided document is not an array, because
    /// the errors will be caught during decode.
    ///
    /// ```
    /// # use rawbson::Array;
    /// // Represents the document {"0": null, "X": 514}
    /// let bson = b"\x0f\0\0\0\x0A0\0\x10X\0\x02\x02\0\0\0";
    /// let arr = unsafe { Array::new_unchecked(bson) };
    /// let mut arriter = arr.into_iter();
    /// assert!(arriter.next().unwrap().and_then(|b| b.as_null()).is_ok());
    /// assert!(arriter.next().unwrap().is_err());
    /// assert!(arriter.next().is_none());
    /// ```
    ///
    /// # Bad:
    ///
    /// The following, however, indicates the wrong size for the document, and is
    /// therefore unsound.
    ///
    /// ```
    /// # use rawbson::Array;
    /// // Contains a length indicator, that is longer than the array
    /// let invalid = b"\x06\0\0\0\0";
    /// let arr: &Array = unsafe { Array::new_unchecked(invalid) };
    /// ```
    pub unsafe fn new_unchecked(data: &[u8]) -> &Array {
        #[allow(unused_unsafe)]
        let doc = unsafe { Doc::new_unchecked(data) };
        Array::from_doc(doc)
    }

    pub fn from_doc(doc: &Doc) -> &Array {
        // SAFETY: Array layout matches Doc layout
        unsafe { &*(doc as *const Doc as *const Array) }
    }

    pub fn get(&self, index: usize) -> OptResult<elem::Element<'_>> {
        self.into_iter().nth(index).transpose()
    }

    fn get_with<'a, T>(
        &'a self,
        index: usize,
        f: impl FnOnce(elem::Element<'a>) -> RawResult<T>,
    ) -> OptResult<T> {
        self.get(index)?.map(f).transpose()
    }

    pub fn get_f64(&self, index: usize) -> OptResult<f64> {
        self.get_with(index, elem::Element::as_f64)
    }

    pub fn get_str(&self, index: usize) -> OptResult<&str> {
        self.get_with(index, elem::Element::as_str)
    }

    pub fn get_document(&self, index: usize) -> OptResult<&Doc> {
        self.get_with(index, elem::Element::as_document)
    }

    pub fn get_array(&self, index: usize) -> OptResult<&Array> {
        self.get_with(index, elem::Element::as_array)
    }

    pub fn get_binary(&self, index: usize) -> OptResult<elem::RawBsonBinary<'_>> {
        self.get_with(index, elem::Element::as_binary)
    }

    pub fn get_object_id(&self, index: usize) -> OptResult<oid::ObjectId> {
        self.get_with(index, elem::Element::as_object_id)
    }

    pub fn get_bool(&self, index: usize) -> OptResult<bool> {
        self.get_with(index, elem::Element::as_bool)
    }

    pub fn get_datetime(&self, index: usize) -> OptResult<DateTime<Utc>> {
        self.get_with(index, elem::Element::as_datetime)
    }

    pub fn get_null(&self, index: usize) -> OptResult<()> {
        self.get_with(index, elem::Element::as_null)
    }

    pub fn get_regex(&self, index: usize) -> OptResult<elem::RawBsonRegex<'_>> {
        self.get_with(index, elem::Element::as_regex)
    }

    pub fn get_javascript(&self, index: usize) -> OptResult<&str> {
        self.get_with(index, elem::Element::as_javascript)
    }

    pub fn get_symbol(&self, index: usize) -> OptResult<&str> {
        self.get_with(index, elem::Element::as_symbol)
    }

    pub fn get_javascript_with_scope(&self, index: usize) -> OptResult<(&str, &Doc)> {
        self.get_with(index, elem::Element::as_javascript_with_scope)
    }

    pub fn get_i32(&self, index: usize) -> OptResult<i32> {
        self.get_with(index, elem::Element::as_i32)
    }

    pub fn get_timestamp(&self, index: usize) -> OptResult<elem::RawBsonTimestamp<'_>> {
        self.get_with(index, elem::Element::as_timestamp)
    }

    pub fn get_i64(&self, index: usize) -> OptResult<i64> {
        self.get_with(index, elem::Element::as_i64)
    }

    pub fn to_vec(&self) -> RawResult<Vec<elem::Element<'_>>> {
        self.into_iter().collect()
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.doc.as_bytes()
    }
}

impl TryFrom<&Array> for Vec<Bson> {
    type Error = RawError;

    fn try_from(arr: &Array) -> RawResult<Vec<Bson>> {
        arr.into_iter()
            .map(|result| {
                let rawbson = result?;
                Bson::try_from(rawbson)
            })
            .collect()
    }
}

impl<'a> IntoIterator for &'a Array {
    type IntoIter = ArrayIter<'a>;
    type Item = RawResult<elem::Element<'a>>;

    fn into_iter(self) -> ArrayIter<'a> {
        ArrayIter {
            dociter: self.doc.into_iter(),
            index: 0,
        }
    }
}

pub struct ArrayIter<'a> {
    dociter: DocIter<'a>,
    index: usize,
}

impl<'a> Iterator for ArrayIter<'a> {
    type Item = RawResult<elem::Element<'a>>;

    fn next(&mut self) -> Option<RawResult<elem::Element<'a>>> {
        let value = self.dociter.next().map(|result| {
            let (key, bson) = match result {
                Ok(value) => value,
                Err(err) => return Err(err),
            };

            let index: usize = key
                .parse()
                .map_err(|_| RawError::MalformedValue("non-integer array index found".into()))?;

            if index == self.index {
                Ok(bson)
            } else {
                Err(RawError::MalformedValue("wrong array index found".into()))
            }
        });
        self.index += 1;
        value
    }
}
/// Given a 4 byte u8 slice, return an i32 calculated from the bytes in
/// little endian order
///
/// # Panics
///
/// This function panics if given a slice that is not four bytes long.
fn i32_from_slice(val: &[u8]) -> i32 {
    i32::from_le_bytes(val.try_into().expect("i32 is four bytes"))
}

/// Given an 8 byte u8 slice, return an i64 calculated from the bytes in
/// little endian order
///
/// # Panics
///
/// This function panics if given a slice that is not eight bytes long.
fn i64_from_slice(val: &[u8]) -> i64 {
    i64::from_le_bytes(val.try_into().expect("i64 is eight bytes"))
}

/// Given a 4 byte u8 slice, return a u32 calculated from the bytes in
/// little endian order
///
/// # Panics
///
/// This function panics if given a slice that is not four bytes long.
fn u32_from_slice(val: &[u8]) -> u32 {
    u32::from_le_bytes(val.try_into().expect("u32 is four bytes"))
}

fn d128_from_slice(val: &[u8]) -> Decimal128 {
    // TODO: Handle Big Endian platforms
    let d =
        unsafe { decimal::d128::from_raw_bytes(val.try_into().expect("d128 is sixteen bytes")) };
    Decimal128::from(d)
}

fn read_nullterminated(buf: &[u8]) -> RawResult<&str> {
    let mut splits = buf.splitn(2, |x| *x == 0);
    let value = splits
        .next()
        .ok_or_else(|| RawError::MalformedValue("no value".into()))?;
    if splits.next().is_some() {
        Ok(try_to_str(value)?)
    } else {
        Err(RawError::MalformedValue("expected null terminator".into()))
    }
}

fn read_lenencoded(buf: &[u8]) -> RawResult<&str> {
    let length = i32_from_slice(&buf[..4]);
    assert!(buf.len() as i32 >= length + 4);
    try_to_str(&buf[4..4 + length as usize - 1])
}

fn try_to_str(data: &[u8]) -> RawResult<&str> {
    match std::str::from_utf8(data) {
        Ok(s) => Ok(s),
        Err(_) => Err(RawError::Utf8EncodingError(data.into())),
    }
}

pub type DocRef<'a> = &'a Doc;

#[cfg(test)]
mod tests {
    use super::*;
    use bson::{doc, spec::BinarySubtype, Binary, Bson, JavaScriptCodeWithScope, Regex, Timestamp};
    use chrono::TimeZone;

    fn to_bytes(doc: &bson::Document) -> Vec<u8> {
        let mut docbytes = Vec::new();
        doc.to_writer(&mut docbytes).unwrap();
        docbytes
    }

    #[test]
    fn string_from_document() {
        let docbytes = to_bytes(&doc! {
            "this": "first",
            "that": "second",
            "something": "else",
        });
        let rawdoc = Doc::new(&docbytes).unwrap();
        assert_eq!(
            rawdoc.get("that").unwrap().unwrap().as_str().unwrap(),
            "second",
        );
    }

    #[test]
    fn nested_document() {
        let docbytes = to_bytes(&doc! {
            "outer": {
                "inner": "surprise",
            },
        });
        let rawdoc = Doc::new(&docbytes).unwrap();
        assert_eq!(
            rawdoc
                .get("outer")
                .expect("get doc result")
                .expect("get doc option")
                .as_document()
                .expect("as doc")
                .get("inner")
                .expect("get str result")
                .expect("get str option")
                .as_str()
                .expect("as str"),
            "surprise",
        );
    }

    #[test]
    fn iterate() {
        let docbytes = to_bytes(&doc! {
            "apples": "oranges",
            "peanut butter": "chocolate",
            "easy as": {"do": 1, "re": 2, "mi": 3},
        });
        let rawdoc = Doc::new(&docbytes).expect("malformed bson document");
        let mut dociter = rawdoc.into_iter();
        let next = dociter.next().expect("no result").expect("invalid bson");
        assert_eq!(next.0, "apples");
        assert_eq!(next.1.as_str().expect("result was not a str"), "oranges");
        let next = dociter.next().expect("no result").expect("invalid bson");
        assert_eq!(next.0, "peanut butter");
        assert_eq!(next.1.as_str().expect("result was not a str"), "chocolate");
        let next = dociter.next().expect("no result").expect("invalid bson");
        assert_eq!(next.0, "easy as");
        let _doc = next.1.as_document().expect("result was a not a document");
        let next = dociter.next();
        assert!(next.is_none());
    }

    #[test]
    fn rawdoc_to_doc() {
        let docbytes = to_bytes(&doc! {
            "f64": 2.5,
            "string": "hello",
            "document": {},
            "array": ["binary", "serialized", "object", "notation"],
            "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1, 2, 3] },
            "object_id": oid::ObjectId::with_bytes([1, 2, 3, 4, 5,6,7,8,9,10, 11,12]),
            "boolean": true,
            "datetime": Utc::now(),
            "null": Bson::Null,
            "regex": Bson::RegularExpression(Regex { pattern: String::from(r"end\s*$"), options: String::from("i")}),
            "javascript": Bson::JavaScriptCode(String::from("console.log(console);")),
            "symbol": Bson::Symbol(String::from("artist-formerly-known-as")),
            "javascript_with_scope": Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope{ code: String::from("console.log(msg);"), scope: doc!{"ok": true}}),
            "int32": 23i32,
            "timestamp": Bson::Timestamp(Timestamp { time: 3542578, increment: 0 }),
            "int64": 46i64,
            "end": "END",
        });

        let rawdoc = Doc::new(&docbytes).expect("invalid document");
        let _doc: bson::Document = rawdoc.try_into().expect("invalid bson");
    }

    #[test]
    fn f64() {
        #![allow(clippy::float_cmp)]

        let rawdoc = DocBuf::from_document(&doc! {"f64": 2.5});
        assert_eq!(
            rawdoc
                .get("f64")
                .expect("error finding key f64")
                .expect("no key f64")
                .as_f64()
                .expect("result was not a f64"),
            2.5,
        );
    }

    #[test]
    fn string() {
        let rawdoc = DocBuf::from_document(&doc! {"string": "hello"});

        assert_eq!(
            rawdoc
                .get("string")
                .expect("error finding key string")
                .expect("no key string")
                .as_str()
                .expect("result was not a string"),
            "hello",
        );
    }
    #[test]
    fn document() {
        let rawdoc = DocBuf::from_document(&doc! {"document": {}});

        let doc = rawdoc
            .get("document")
            .expect("error finding key document")
            .expect("no key document")
            .as_document()
            .expect("result was not a document");
        assert_eq!(&doc.data, [5, 0, 0, 0, 0].as_ref()); // Empty document
    }

    #[test]
    fn array() {
        let rawdoc =
            DocBuf::from_document(&doc! { "array": ["binary", "serialized", "object", "notation"]});

        let array = rawdoc
            .get("array")
            .expect("error finding key array")
            .expect("no key array")
            .as_array()
            .expect("result was not an array");
        assert_eq!(array.get_str(0), Ok(Some("binary")));
        assert_eq!(array.get_str(3), Ok(Some("notation")));
        assert_eq!(array.get_str(4), Ok(None));
    }

    #[test]
    fn binary() {
        let rawdoc = DocBuf::from_document(&doc! {
            "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1u8, 2, 3] }
        });
        let binary: elem::RawBsonBinary<'_> = rawdoc
            .get("binary")
            .expect("error finding key binary")
            .expect("no key binary")
            .as_binary()
            .expect("result was not a binary object");
        assert_eq!(binary.subtype, BinarySubtype::Generic);
        assert_eq!(binary.data, &[1, 2, 3]);
    }

    #[test]
    fn object_id() {
        let rawdoc = DocBuf::from_document(&doc! {
            "object_id": oid::ObjectId::with_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
        });
        let oid = rawdoc
            .get("object_id")
            .expect("error finding key object_id")
            .expect("no key object_id")
            .as_object_id()
            .expect("result was not an object id");
        assert_eq!(oid.to_hex(), "0102030405060708090a0b0c");
    }

    #[test]
    fn boolean() {
        let rawdoc = DocBuf::from_document(&doc! {
            "boolean": true,
        });

        let boolean = rawdoc
            .get("boolean")
            .expect("error finding key boolean")
            .expect("no key boolean")
            .as_bool()
            .expect("result was not boolean");

        assert_eq!(boolean, true);
    }

    #[test]
    fn datetime() {
        let rawdoc = DocBuf::from_document(&doc! {
            "boolean": true,
            "datetime": Utc.ymd(2000,10,31).and_hms(12, 30, 45),
        });
        let datetime = rawdoc
            .get("datetime")
            .expect("error finding key datetime")
            .expect("no key datetime")
            .as_datetime()
            .expect("result was not datetime");
        assert_eq!(datetime.to_rfc3339(), "2000-10-31T12:30:45+00:00");
    }

    #[test]
    fn null() {
        let rawdoc = DocBuf::from_document(&doc! {
            "null": null,
        });
        let () = rawdoc
            .get("null")
            .expect("error finding key null")
            .expect("no key null")
            .as_null()
            .expect("was not null");
    }

    #[test]
    fn regex() {
        let rawdoc = DocBuf::from_document(&doc! {
            "regex": Bson::RegularExpression(Regex { pattern: String::from(r"end\s*$"), options: String::from("i")}),
        });
        let regex = rawdoc
            .get("regex")
            .expect("error finding key regex")
            .expect("no key regex")
            .as_regex()
            .expect("was not regex");
        assert_eq!(regex.pattern, r"end\s*$");
        assert_eq!(regex.options, "i");
    }
    #[test]
    fn javascript() {
        let rawdoc = DocBuf::from_document(&doc! {
            "javascript": Bson::JavaScriptCode(String::from("console.log(console);")),
        });
        let js = rawdoc
            .get("javascript")
            .expect("error finding key javascript")
            .expect("no key javascript")
            .as_javascript()
            .expect("was not javascript");
        assert_eq!(js, "console.log(console);");
    }

    #[test]
    fn symbol() {
        let rawdoc = DocBuf::from_document(&doc! {
            "symbol": Bson::Symbol(String::from("artist-formerly-known-as")),
        });

        let symbol = rawdoc
            .get("symbol")
            .expect("error finding key symbol")
            .expect("no key symbol")
            .as_symbol()
            .expect("was not symbol");
        assert_eq!(symbol, "artist-formerly-known-as");
    }

    #[test]
    fn javascript_with_scope() {
        let rawdoc = DocBuf::from_document(&doc! {
            "javascript_with_scope": Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope{ code: String::from("console.log(msg);"), scope: doc!{"ok": true}}),
        });
        let (js, scopedoc) = rawdoc
            .get("javascript_with_scope")
            .expect("error finding key javascript_with_scope")
            .expect("no key javascript_with_scope")
            .as_javascript_with_scope()
            .expect("was not javascript with scope");
        assert_eq!(js, "console.log(msg);");
        let (scope_key, scope_value_bson) = scopedoc
            .into_iter()
            .next()
            .expect("no next value in scope")
            .expect("invalid element");
        assert_eq!(scope_key, "ok");
        let scope_value = scope_value_bson.as_bool().expect("not a boolean");
        assert_eq!(scope_value, true);
    }

    #[test]
    fn int32() {
        let rawdoc = DocBuf::from_document(&doc! {
            "int32": 23i32,
        });
        let int32 = rawdoc
            .get("int32")
            .expect("error finding key int32")
            .expect("no key int32")
            .as_i32()
            .expect("was not int32");
        assert_eq!(int32, 23i32);
    }

    #[test]
    fn timestamp() {
        let rawdoc = DocBuf::from_document(&doc! {
            "timestamp": Bson::Timestamp(Timestamp { time: 3542578, increment: 7 }),
        });
        let ts = rawdoc
            .get("timestamp")
            .expect("error finding key timestamp")
            .expect("no key timestamp")
            .as_timestamp()
            .expect("was not a timestamp");

        assert_eq!(ts.increment(), 7);
        assert_eq!(ts.time(), 3542578);
    }

    #[test]
    fn int64() {
        let rawdoc = DocBuf::from_document(&doc! {
            "int64": 46i64,
        });
        let int64 = rawdoc
            .get("int64")
            .expect("error finding key int64")
            .expect("no key int64")
            .as_i64()
            .expect("was not int64");
        assert_eq!(int64, 46i64);
    }
    #[test]
    fn document_iteration() {
        let docbytes = to_bytes(&doc! {
            "f64": 2.5,
            "string": "hello",
            "document": {},
            "array": ["binary", "serialized", "object", "notation"],
            "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1u8, 2, 3] },
            "object_id": oid::ObjectId::with_bytes([1, 2, 3, 4, 5,6,7,8,9,10, 11,12]),
            "boolean": true,
            "datetime": Utc::now(),
            "null": Bson::Null,
            "regex": Bson::RegularExpression(Regex { pattern: String::from(r"end\s*$"), options: String::from("i")}),
            "javascript": Bson::JavaScriptCode(String::from("console.log(console);")),
            "symbol": Bson::Symbol(String::from("artist-formerly-known-as")),
            "javascript_with_scope": Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope{ code: String::from("console.log(msg);"), scope: doc!{"ok": true}}),
            "int32": 23i32,
            "timestamp": Bson::Timestamp(Timestamp { time: 3542578, increment: 0 }),
            "int64": 46i64,
            "end": "END",
        });
        let rawdoc = unsafe { Doc::new_unchecked(&docbytes) };

        assert_eq!(
            rawdoc
                .into_iter()
                .collect::<Result<Vec<(&str, _)>, RawError>>()
                .expect("collecting iterated doc")
                .len(),
            17
        );
        let end = rawdoc
            .get("end")
            .expect("error finding key end")
            .expect("no key end")
            .as_str()
            .expect("was not str");
        assert_eq!(end, "END");
    }

    #[test]
    fn into_bson_conversion() {
        let docbytes = to_bytes(&doc! {
            "f64": 2.5,
            "string": "hello",
            "document": {},
            "array": ["binary", "serialized", "object", "notation"],
            "object_id": oid::ObjectId::with_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
            "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1u8, 2, 3] },
            "boolean": false,
        });
        let rawbson = elem::Element::new(ElementType::EmbeddedDocument, &docbytes);
        let b: Bson = rawbson.try_into().expect("invalid bson");
        let doc = b.as_document().expect("not a document");
        assert_eq!(*doc.get("f64").expect("f64 not found"), Bson::Double(2.5));
        assert_eq!(
            *doc.get("string").expect("string not found"),
            Bson::String(String::from("hello"))
        );
        assert_eq!(
            *doc.get("document").expect("document not found"),
            Bson::Document(doc! {})
        );
        assert_eq!(
            *doc.get("array").expect("array not found"),
            Bson::Array(
                vec!["binary", "serialized", "object", "notation"]
                    .into_iter()
                    .map(|s| Bson::String(String::from(s)))
                    .collect()
            )
        );
        assert_eq!(
            *doc.get("object_id").expect("object_id not found"),
            Bson::ObjectId(oid::ObjectId::with_bytes([
                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
            ]))
        );
        assert_eq!(
            *doc.get("binary").expect("binary not found"),
            Bson::Binary(Binary {
                subtype: BinarySubtype::Generic,
                bytes: vec![1, 2, 3]
            })
        );
        assert_eq!(
            *doc.get("boolean").expect("boolean not found"),
            Bson::Boolean(false)
        );
    }
}

#[cfg(test)]
mod proptests {
    use proptest::prelude::*;
    use std::convert::TryInto;

    use super::DocBuf;
    use crate::props::arbitrary_bson;
    use bson::doc;

    fn to_bytes(doc: &bson::Document) -> Vec<u8> {
        let mut docbytes = Vec::new();
        doc.to_writer(&mut docbytes).unwrap();
        docbytes
    }

    proptest! {
        #[test]
        fn no_crashes(s: Vec<u8>) {
            let _ = DocBuf::new(s);
        }

        #[test]
        fn roundtrip_bson(bson in arbitrary_bson()) {
            println!("{:?}", bson);
            let doc = doc!{"bson": bson};
            let raw = to_bytes(&doc);
            let raw = DocBuf::new(raw);
            prop_assert!(raw.is_ok());
            let raw = raw.unwrap();
            let roundtrip: Result<bson::Document, _> = raw.try_into();
            prop_assert!(roundtrip.is_ok());
            let roundtrip = roundtrip.unwrap();
            prop_assert_eq!(doc, roundtrip);
        }
    }
}