use std::{
borrow::{Borrow, Cow},
convert::{TryFrom, TryInto},
iter::FromIterator,
ops::Deref,
};
use serde::{Deserialize, Serialize};
use crate::{
de::MIN_BSON_DOCUMENT_SIZE,
spec::BinarySubtype,
Document,
RawBinaryRef,
RawJavaScriptCodeWithScopeRef,
};
use super::{
bson::RawBson,
iter::Iter,
serde::OwnedOrBorrowedRawDocument,
Error,
ErrorKind,
RawBsonRef,
RawDocument,
RawIter,
Result,
};
#[derive(Clone, PartialEq)]
pub struct RawDocumentBuf {
data: Vec<u8>,
}
impl RawDocumentBuf {
pub fn new() -> RawDocumentBuf {
let mut data = Vec::new();
data.extend(MIN_BSON_DOCUMENT_SIZE.to_le_bytes());
data.push(0);
Self { data }
}
pub fn from_bytes(data: Vec<u8>) -> Result<RawDocumentBuf> {
let _ = RawDocument::from_bytes(data.as_slice())?;
Ok(Self { data })
}
pub fn from_document(doc: &Document) -> Result<RawDocumentBuf> {
let mut data = Vec::new();
doc.to_writer(&mut data).map_err(|e| Error {
key: None,
kind: ErrorKind::MalformedValue {
message: e.to_string(),
},
})?;
Ok(Self { data })
}
pub fn iter(&self) -> Iter<'_> {
Iter::new(self)
}
pub fn iter_elements(&self) -> RawIter<'_> {
RawIter::new(self)
}
pub fn into_bytes(self) -> Vec<u8> {
self.data
}
pub fn append(&mut self, key: impl AsRef<str>, value: impl Into<RawBson>) {
fn append_string(doc: &mut RawDocumentBuf, value: &str) {
doc.data
.extend(((value.as_bytes().len() + 1) as i32).to_le_bytes());
doc.data.extend(value.as_bytes());
doc.data.push(0);
}
fn append_cstring(doc: &mut RawDocumentBuf, value: &str) {
if value.contains('\0') {
panic!("cstr includes interior null byte: {}", value)
}
doc.data.extend(value.as_bytes());
doc.data.push(0);
}
let original_len = self.data.len();
append_cstring(self, key.as_ref());
let value = value.into();
let element_type = value.element_type();
match value {
RawBson::Int32(i) => {
self.data.extend(i.to_le_bytes());
}
RawBson::String(s) => {
append_string(self, s.as_str());
}
RawBson::Document(d) => {
self.data.extend(d.into_bytes());
}
RawBson::Array(a) => {
self.data.extend(a.into_vec());
}
RawBson::Binary(b) => {
let len = RawBinaryRef {
bytes: b.bytes.as_slice(),
subtype: b.subtype,
}
.len();
self.data.extend(len.to_le_bytes());
self.data.push(b.subtype.into());
if let BinarySubtype::BinaryOld = b.subtype {
self.data.extend((len - 4).to_le_bytes())
}
self.data.extend(b.bytes);
}
RawBson::Boolean(b) => {
self.data.push(b as u8);
}
RawBson::DateTime(dt) => {
self.data.extend(dt.timestamp_millis().to_le_bytes());
}
RawBson::DbPointer(dbp) => {
append_string(self, dbp.namespace.as_str());
self.data.extend(dbp.id.bytes());
}
RawBson::Decimal128(d) => {
self.data.extend(d.bytes());
}
RawBson::Double(d) => {
self.data.extend(d.to_le_bytes());
}
RawBson::Int64(i) => {
self.data.extend(i.to_le_bytes());
}
RawBson::RegularExpression(re) => {
append_cstring(self, re.pattern.as_str());
append_cstring(self, re.options.as_str());
}
RawBson::JavaScriptCode(js) => {
append_string(self, js.as_str());
}
RawBson::JavaScriptCodeWithScope(code_w_scope) => {
let len = RawJavaScriptCodeWithScopeRef {
code: code_w_scope.code.as_str(),
scope: &code_w_scope.scope,
}
.len();
self.data.extend(len.to_le_bytes());
append_string(self, code_w_scope.code.as_str());
self.data.extend(code_w_scope.scope.into_bytes());
}
RawBson::Timestamp(ts) => {
self.data.extend(ts.to_le_i64().to_le_bytes());
}
RawBson::ObjectId(oid) => {
self.data.extend(oid.bytes());
}
RawBson::Symbol(s) => {
append_string(self, s.as_str());
}
RawBson::Null | RawBson::Undefined | RawBson::MinKey | RawBson::MaxKey => {}
}
self.data[original_len - 1] = element_type as u8;
self.data.push(0);
let new_len = (self.data.len() as i32).to_le_bytes();
self.data[0..4].copy_from_slice(&new_len);
}
pub fn to_document(&self) -> Result<Document> {
self.as_ref().try_into()
}
}
impl Default for RawDocumentBuf {
fn default() -> Self {
Self::new()
}
}
impl<'de> Deserialize<'de> for RawDocumentBuf {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(OwnedOrBorrowedRawDocument::deserialize(deserializer)?.into_owned())
}
}
impl Serialize for RawDocumentBuf {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let doc: &RawDocument = self.deref();
doc.serialize(serializer)
}
}
impl std::fmt::Debug for RawDocumentBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RawDocumentBuf")
.field("data", &hex::encode(&self.data))
.finish()
}
}
impl<'a> From<RawDocumentBuf> for Cow<'a, RawDocument> {
fn from(rd: RawDocumentBuf) -> Self {
Cow::Owned(rd)
}
}
impl<'a> From<&'a RawDocumentBuf> for Cow<'a, RawDocument> {
fn from(rd: &'a RawDocumentBuf) -> Self {
Cow::Borrowed(rd.as_ref())
}
}
impl TryFrom<RawDocumentBuf> for Document {
type Error = Error;
fn try_from(raw: RawDocumentBuf) -> Result<Document> {
Document::try_from(raw.as_ref())
}
}
impl TryFrom<&Document> for RawDocumentBuf {
type Error = Error;
fn try_from(doc: &Document) -> Result<RawDocumentBuf> {
RawDocumentBuf::from_document(doc)
}
}
impl<'a> IntoIterator for &'a RawDocumentBuf {
type IntoIter = Iter<'a>;
type Item = Result<(&'a str, RawBsonRef<'a>)>;
fn into_iter(self) -> Iter<'a> {
Iter::new(self)
}
}
impl AsRef<RawDocument> for RawDocumentBuf {
fn as_ref(&self) -> &RawDocument {
RawDocument::new_unchecked(&self.data)
}
}
impl Deref for RawDocumentBuf {
type Target = RawDocument;
fn deref(&self) -> &Self::Target {
RawDocument::new_unchecked(&self.data)
}
}
impl Borrow<RawDocument> for RawDocumentBuf {
fn borrow(&self) -> &RawDocument {
self.deref()
}
}
impl<S: AsRef<str>, T: Into<RawBson>> FromIterator<(S, T)> for RawDocumentBuf {
fn from_iter<I: IntoIterator<Item = (S, T)>>(iter: I) -> Self {
let mut buf = RawDocumentBuf::new();
for (k, v) in iter {
buf.append(k, v);
}
buf
}
}