diff --git a/serde-tests/json.rs b/serde-tests/json.rs new file mode 100644 index 00000000..473b3f21 --- /dev/null +++ b/serde-tests/json.rs @@ -0,0 +1,132 @@ +use pretty_assertions::assert_eq; +use serde_json::json; + +use super::AllTypes; + +use bson::{doc, Bson, JavaScriptCodeWithScope, RawArrayBuf, RawBson, RawDocumentBuf}; + +use serde::{Deserialize, Serialize}; + +#[test] +fn all_types_json() { + let (mut v, _) = AllTypes::fixtures(); + + let code = match v.code { + Bson::JavaScriptCode(ref c) => c.clone(), + c => panic!("expected code, found {:?}", c), + }; + + let code_w_scope = JavaScriptCodeWithScope { + code: "hello world".to_string(), + scope: doc! { "x": 1 }, + }; + let scope_json = serde_json::json!({ "x": 1 }); + v.code_w_scope = code_w_scope.clone(); + + let json = serde_json::json!({ + "x": 1, + "y": 2, + "s": "oke", + "array": vec![ + serde_json::json!(true), + serde_json::json!("oke".to_string()), + serde_json::json!({ "12": 24 }), + ], + "bson": 1234.5, + "oid": { "$oid": v.oid.to_hex() }, + "null": serde_json::Value::Null, + "subdoc": { "k": true, "b": { "hello": "world" } }, + "b": true, + "d": 12.5, + "binary": v.binary.bytes, + "binary_old": { "$binary": { "base64": base64::encode(&v.binary_old.bytes), "subType": "02" } }, + "binary_other": { "$binary": { "base64": base64::encode(&v.binary_old.bytes), "subType": "81" } }, + "date": { "$date": { "$numberLong": v.date.timestamp_millis().to_string() } }, + "regex": { "$regularExpression": { "pattern": v.regex.pattern, "options": v.regex.options } }, + "ts": { "$timestamp": { "t": 123, "i": 456 } }, + "i": { "a": v.i.a, "b": v.i.b }, + "undefined": { "$undefined": true }, + "code": { "$code": code }, + "code_w_scope": { "$code": code_w_scope.code, "$scope": scope_json }, + "decimal": { "$numberDecimalBytes": v.decimal.bytes() }, + "symbol": { "$symbol": "ok" }, + "min_key": { "$minKey": 1 }, + "max_key": { "$maxKey": 1 }, + }); + + assert_eq!(serde_json::to_value(&v).unwrap(), json); +} + +#[test] +fn owned_raw_bson() { + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct Foo { + doc_buf: RawDocumentBuf, + array_buf: RawArrayBuf, + bson_array: RawBson, + bson_doc: RawBson, + bson_integer: RawBson, + bson_string: RawBson, + bson_bool: RawBson, + bson_null: RawBson, + bson_float: RawBson, + } + + let json = json!({ + "doc_buf": { + "a": "key", + "number": 12, + "bool": false, + "nu": null + }, + "array_buf": [ + json!(1), + json!("string"), + ], + "bson_array": [ + json!(1), + json!("string"), + ], + "bson_doc": { + "first": true, + "second": "string", + }, + "bson_integer": 12, + "bson_string": "String", + "bson_bool": true, + "bson_null": null, + "bson_float": 123.4 + }); + + let mut doc_buf = RawDocumentBuf::new(); + doc_buf.append("a", "key"); + doc_buf.append("number", 12); + doc_buf.append("bool", false); + doc_buf.append("nu", RawBson::Null); + + let mut array_buf = RawArrayBuf::new(); + array_buf.push(1); + array_buf.push("string"); + + let mut bson_doc = RawDocumentBuf::new(); + bson_doc.append("first", true); + bson_doc.append("second", "string"); + + let expected = Foo { + doc_buf, + array_buf: array_buf.clone(), + bson_array: RawBson::Array(array_buf), + bson_doc: RawBson::Document(bson_doc), + bson_integer: RawBson::Int32(12), + bson_string: RawBson::String("String".to_string()), + bson_bool: RawBson::Boolean(true), + bson_null: RawBson::Null, + bson_float: RawBson::Double(123.4), + }; + + let f: Foo = serde_json::from_value(json.clone()).unwrap(); + assert_eq!(f, expected); + + let round_trip = serde_json::to_value(&f).unwrap(); + assert_eq!(round_trip, json); +} diff --git a/serde-tests/test.rs b/serde-tests/test.rs index 85c62ace..eeae4500 100644 --- a/serde-tests/test.rs +++ b/serde-tests/test.rs @@ -1,6 +1,7 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::vec_init_then_push)] +mod json; mod options; use pretty_assertions::assert_eq; @@ -14,6 +15,7 @@ use serde::{ use std::{ borrow::Cow, collections::{BTreeMap, HashSet}, + iter::FromIterator, }; use bson::{ @@ -30,13 +32,15 @@ use bson::{ Document, JavaScriptCodeWithScope, RawArray, - RawBinary, + RawArrayBuf, + RawBinaryRef, RawBson, - RawDbPointer, + RawBsonRef, + RawDbPointerRef, RawDocument, RawDocumentBuf, - RawJavaScriptCodeWithScope, - RawRegex, + RawJavaScriptCodeWithScopeRef, + RawRegexRef, Regex, SerializerOptions, Timestamp, @@ -791,16 +795,16 @@ fn raw_binary() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Foo<'a> { #[serde(borrow)] - generic: RawBinary<'a>, + generic: RawBinaryRef<'a>, #[serde(borrow)] - old: RawBinary<'a>, + old: RawBinaryRef<'a>, #[serde(borrow)] - uuid: RawBinary<'a>, + uuid: RawBinaryRef<'a>, #[serde(borrow)] - other: RawBinary<'a>, + other: RawBinaryRef<'a>, } let bytes = bson::to_vec(&doc! { @@ -828,7 +832,7 @@ fn raw_regex() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Foo<'a> { #[serde(borrow)] - r: RawRegex<'a>, + r: RawRegexRef<'a>, } let bytes = bson::to_vec(&doc! { @@ -847,7 +851,7 @@ fn raw_code_w_scope() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Foo<'a> { #[serde(borrow)] - r: RawJavaScriptCodeWithScope<'a>, + r: RawJavaScriptCodeWithScopeRef<'a>, } let bytes = bson::to_vec(&doc! { @@ -866,7 +870,7 @@ fn raw_db_pointer() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Foo<'a> { #[serde(borrow)] - a: RawDbPointer<'a>, + a: RawDbPointerRef<'a>, } // From the "DBpointer" bson corpus test @@ -1018,56 +1022,6 @@ fn all_types() { run_test(&v, &doc, "all types"); } -#[test] -fn all_types_json() { - let (mut v, _) = AllTypes::fixtures(); - - let code = match v.code { - Bson::JavaScriptCode(ref c) => c.clone(), - c => panic!("expected code, found {:?}", c), - }; - - let code_w_scope = JavaScriptCodeWithScope { - code: "hello world".to_string(), - scope: doc! { "x": 1 }, - }; - let scope_json = serde_json::json!({ "x": 1 }); - v.code_w_scope = code_w_scope.clone(); - - let json = serde_json::json!({ - "x": 1, - "y": 2, - "s": "oke", - "array": vec![ - serde_json::json!(true), - serde_json::json!("oke".to_string()), - serde_json::json!({ "12": 24 }), - ], - "bson": 1234.5, - "oid": { "$oid": v.oid.to_hex() }, - "null": serde_json::Value::Null, - "subdoc": { "k": true, "b": { "hello": "world" } }, - "b": true, - "d": 12.5, - "binary": v.binary.bytes, - "binary_old": { "$binary": { "base64": base64::encode(&v.binary_old.bytes), "subType": "02" } }, - "binary_other": { "$binary": { "base64": base64::encode(&v.binary_old.bytes), "subType": "81" } }, - "date": { "$date": { "$numberLong": v.date.timestamp_millis().to_string() } }, - "regex": { "$regularExpression": { "pattern": v.regex.pattern, "options": v.regex.options } }, - "ts": { "$timestamp": { "t": 123, "i": 456 } }, - "i": { "a": v.i.a, "b": v.i.b }, - "undefined": { "$undefined": true }, - "code": { "$code": code }, - "code_w_scope": { "$code": code_w_scope.code, "$scope": scope_json }, - "decimal": { "$numberDecimalBytes": v.decimal.bytes() }, - "symbol": { "$symbol": "ok" }, - "min_key": { "$minKey": 1 }, - "max_key": { "$maxKey": 1 }, - }); - - assert_eq!(serde_json::to_value(&v).unwrap(), json); -} - #[test] fn all_types_rmp() { let (v, _) = AllTypes::fixtures(); @@ -1082,18 +1036,18 @@ fn all_raw_types_rmp() { #[derive(Debug, Serialize, Deserialize, PartialEq)] struct AllRawTypes<'a> { #[serde(borrow)] - bson: RawBson<'a>, + bson: RawBsonRef<'a>, #[serde(borrow)] document: &'a RawDocument, #[serde(borrow)] array: &'a RawArray, buf: RawDocumentBuf, #[serde(borrow)] - binary: RawBinary<'a>, + binary: RawBinaryRef<'a>, #[serde(borrow)] - code_w_scope: RawJavaScriptCodeWithScope<'a>, + code_w_scope: RawJavaScriptCodeWithScopeRef<'a>, #[serde(borrow)] - regex: RawRegex<'a>, + regex: RawRegexRef<'a>, } let doc_bytes = bson::to_vec(&doc! { @@ -1111,7 +1065,7 @@ fn all_raw_types_rmp() { } }) .unwrap(); - let doc_buf = RawDocumentBuf::new(doc_bytes).unwrap(); + let doc_buf = RawDocumentBuf::from_bytes(doc_bytes).unwrap(); let document = &doc_buf; let array = document.get_array("array").unwrap(); @@ -1280,6 +1234,59 @@ fn serde_with_uuid() { run_test(&f, &expected, "serde_with - uuid"); } +#[test] +fn owned_raw_types() { + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct Foo { + subdoc: RawDocumentBuf, + array: RawArrayBuf, + } + + let oid = ObjectId::new(); + let dt = DateTime::now(); + + let f = Foo { + subdoc: RawDocumentBuf::from_iter([ + ("a key", RawBson::String("a value".to_string())), + ("an objectid", RawBson::ObjectId(oid)), + ("a date", RawBson::DateTime(dt)), + ]), + array: RawArrayBuf::from_iter([ + RawBson::String("a string".to_string()), + RawBson::ObjectId(oid), + RawBson::DateTime(dt), + ]), + }; + + let expected = doc! { + "subdoc": { + "a key": "a value", + "an objectid": oid, + "a date": dt, + }, + "array": [ + "a string", + oid, + dt + ] + }; + + // TODO: RUST-1111 + // can't use run_test here because deserializing RawDocumentBuf and RawArrayBuf + // from Bson or Document currently don't work. + + let bytes = bson::to_vec(&expected).unwrap(); + + let deserialized: Foo = bson::from_slice(bytes.as_slice()).unwrap(); + assert_eq!(deserialized, f); + + let serialized = bson::to_document(&deserialized).unwrap(); + assert_eq!(serialized, expected); + + let serialized_bytes = bson::to_vec(&deserialized).unwrap(); + assert_eq!(serialized_bytes, bytes); +} + #[test] fn hint_cleared() { #[derive(Debug, Serialize, Deserialize)] @@ -1287,7 +1294,7 @@ fn hint_cleared() { #[serde(borrow)] doc: &'a RawDocument, #[serde(borrow)] - binary: RawBinary<'a>, + binary: RawBinaryRef<'a>, } let binary_value = Binary { @@ -1301,7 +1308,7 @@ fn hint_cleared() { let bytes = bson::to_vec(&doc_value).unwrap(); - let doc = RawDocument::new(&bytes).unwrap(); + let doc = RawDocument::from_bytes(&bytes).unwrap(); let binary = doc.get_binary("binary").unwrap(); let f = Foo { doc, binary }; @@ -1315,13 +1322,13 @@ fn hint_cleared() { #[test] fn non_human_readable() { let bytes = vec![1, 2, 3, 4]; - let binary = RawBinary { + let binary = RawBinaryRef { bytes: &bytes, subtype: BinarySubtype::BinaryOld, }; let doc_bytes = bson::to_vec(&doc! { "a": "b", "array": [1, 2, 3] }).unwrap(); - let doc = RawDocument::new(doc_bytes.as_slice()).unwrap(); + let doc = RawDocument::from_bytes(doc_bytes.as_slice()).unwrap(); let arr = doc.get_array("array").unwrap(); let oid = ObjectId::new(); let uuid = Uuid::new(); @@ -1329,7 +1336,7 @@ fn non_human_readable() { #[derive(Debug, Deserialize, Serialize)] struct Foo<'a> { #[serde(borrow)] - binary: RawBinary<'a>, + binary: RawBinaryRef<'a>, #[serde(borrow)] doc: &'a RawDocument, #[serde(borrow)] diff --git a/src/bson.rs b/src/bson.rs index 11ee714c..d8d7398f 100644 --- a/src/bson.rs +++ b/src/bson.rs @@ -34,6 +34,7 @@ use crate::{ oid::{self, ObjectId}, spec::{BinarySubtype, ElementType}, Decimal128, + RawBinaryRef, }; /// Possible BSON value types. @@ -338,6 +339,12 @@ impl From for Bson { } } +impl From for Bson { + fn from(d: Decimal128) -> Self { + Bson::Decimal128(d) + } +} + impl From> for Bson where T: Into, @@ -732,10 +739,7 @@ impl Bson { if let Ok(regex) = doc.get_document("$regularExpression") { if let Ok(pattern) = regex.get_str("pattern") { if let Ok(options) = regex.get_str("options") { - return Bson::RegularExpression(Regex::new( - pattern.into(), - options.into(), - )); + return Bson::RegularExpression(Regex::new(pattern, options)); } } } @@ -1014,11 +1018,14 @@ pub struct Regex { } impl Regex { - pub(crate) fn new(pattern: String, options: String) -> Self { - let mut chars: Vec<_> = options.chars().collect(); + pub(crate) fn new(pattern: impl AsRef, options: impl AsRef) -> Self { + let mut chars: Vec<_> = options.as_ref().chars().collect(); chars.sort_unstable(); let options: String = chars.into_iter().collect(); - Self { pattern, options } + Self { + pattern: pattern.as_ref().to_string(), + options, + } } } @@ -1090,6 +1097,13 @@ impl Binary { }) } } + + pub(crate) fn as_raw_binary(&self) -> RawBinaryRef<'_> { + RawBinaryRef { + bytes: self.bytes.as_slice(), + subtype: self.subtype, + } + } } /// Represents a DBPointer. (Deprecated) diff --git a/src/de/mod.rs b/src/de/mod.rs index 3a8fef0d..dcb56d18 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -35,7 +35,7 @@ use std::io::Read; use crate::{ bson::{Array, Binary, Bson, DbPointer, Document, JavaScriptCodeWithScope, Regex, Timestamp}, oid::{self, ObjectId}, - raw::RawBinary, + raw::RawBinaryRef, ser::write_i32, spec::{self, BinarySubtype}, Decimal128, @@ -307,7 +307,7 @@ impl Binary { } } -impl<'a> RawBinary<'a> { +impl<'a> RawBinaryRef<'a> { pub(crate) fn from_slice_with_len_and_payload( mut bytes: &'a [u8], mut len: i32, diff --git a/src/de/raw.rs b/src/de/raw.rs index 05817831..e9405f58 100644 --- a/src/de/raw.rs +++ b/src/de/raw.rs @@ -13,7 +13,7 @@ use serde::{ use crate::{ oid::ObjectId, - raw::{RawBinary, RAW_ARRAY_NEWTYPE, RAW_BSON_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, + raw::{RawBinaryRef, RAW_ARRAY_NEWTYPE, RAW_BSON_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, spec::{BinarySubtype, ElementType}, uuid::UUID_NEWTYPE_NAME, Bson, @@ -163,7 +163,7 @@ impl<'de> Deserializer<'de> { let mut len = self.bytes.slice(4)?; let len = read_i32(&mut len)?; - let doc = RawDocument::new(self.bytes.read_slice(len as usize)?) + let doc = RawDocument::from_bytes(self.bytes.read_slice(len as usize)?) .map_err(Error::custom)?; let access = if is_array { @@ -266,7 +266,7 @@ impl<'de> Deserializer<'de> { visitor.visit_borrowed_bytes(self.bytes.read_slice(len as usize)?) } _ => { - let binary = RawBinary::from_slice_with_len_and_payload( + let binary = RawBinaryRef::from_slice_with_len_and_payload( self.bytes.read_slice(len as usize)?, len, subtype, @@ -648,13 +648,20 @@ impl<'d, 'de> serde::de::Deserializer<'de> for DocumentKeyDeserializer<'d, 'de> } } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn is_human_readable(&self) -> bool { false } forward_to_deserialize_any! { bool char str bytes byte_buf option unit unit_struct string - identifier newtype_struct seq tuple tuple_struct struct map enum + identifier seq tuple tuple_struct struct map enum ignored_any i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 } } @@ -674,13 +681,20 @@ impl<'de> serde::de::Deserializer<'de> for FieldDeserializer { visitor.visit_borrowed_str(self.field_name) } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn is_human_readable(&self) -> bool { false } serde::forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - bytes byte_buf map struct option unit newtype_struct + bytes byte_buf map struct option unit ignored_any unit_struct tuple_struct tuple enum identifier } } @@ -1096,13 +1110,20 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut DateTimeDeserializer { } } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn is_human_readable(&self) -> bool { false } serde::forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - bytes byte_buf map struct option unit newtype_struct + bytes byte_buf map struct option unit ignored_any unit_struct tuple_struct tuple enum identifier } } @@ -1147,13 +1168,13 @@ impl<'de, 'd> serde::de::MapAccess<'de> for BinaryAccess<'d, 'de> { } struct BinaryDeserializer<'a> { - binary: RawBinary<'a>, + binary: RawBinaryRef<'a>, hint: DeserializerHint, stage: BinaryDeserializationStage, } impl<'a> BinaryDeserializer<'a> { - fn new(binary: RawBinary<'a>, hint: DeserializerHint) -> Self { + fn new(binary: RawBinaryRef<'a>, hint: DeserializerHint) -> Self { Self { binary, hint, @@ -1329,13 +1350,20 @@ impl<'de, 'a, 'b> serde::de::Deserializer<'de> for &'b mut CodeWithScopeDeserial } } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn is_human_readable(&self) -> bool { false } serde::forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - bytes byte_buf map struct option unit newtype_struct + bytes byte_buf map struct option unit ignored_any unit_struct tuple_struct tuple enum identifier } } @@ -1438,13 +1466,20 @@ impl<'de, 'a, 'b> serde::de::Deserializer<'de> for &'b mut DbPointerDeserializer } } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn is_human_readable(&self) -> bool { false } serde::forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - bytes byte_buf map struct option unit newtype_struct + bytes byte_buf map struct option unit ignored_any unit_struct tuple_struct tuple enum identifier } } @@ -1646,13 +1681,20 @@ impl<'de, 'a> serde::de::Deserializer<'de> for RawBsonDeserializer<'de> { } } + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + fn is_human_readable(&self) -> bool { false } serde::forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq - bytes byte_buf map struct option unit newtype_struct + bytes byte_buf map struct option unit ignored_any unit_struct tuple_struct tuple enum identifier } } diff --git a/src/de/serde.rs b/src/de/serde.rs index c6b03c3f..fe5042f5 100644 --- a/src/de/serde.rs +++ b/src/de/serde.rs @@ -25,7 +25,7 @@ use crate::{ datetime::DateTime, document::{Document, IntoIter}, oid::ObjectId, - raw::RawBson, + raw::RawBsonRef, spec::BinarySubtype, uuid::UUID_NEWTYPE_NAME, Decimal128, @@ -548,14 +548,14 @@ where } } -pub(crate) fn convert_unsigned_to_signed_raw<'a, E>(value: u64) -> Result, E> +pub(crate) fn convert_unsigned_to_signed_raw<'a, E>(value: u64) -> Result, E> where E: Error, { let bi = _convert_unsigned(value)?; match bi { - BsonInteger::Int32(i) => Ok(RawBson::Int32(i)), - BsonInteger::Int64(i) => Ok(RawBson::Int64(i)), + BsonInteger::Int32(i) => Ok(RawBsonRef::Int32(i)), + BsonInteger::Int64(i) => Ok(RawBsonRef::Int64(i)), } } diff --git a/src/lib.rs b/src/lib.rs index 164b4456..8d309e4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -272,13 +272,14 @@ pub use self::{ bson::{Array, Binary, Bson, DbPointer, Document, JavaScriptCodeWithScope, Regex, Timestamp}, datetime::DateTime, de::{ - from_bson, from_bson_with_options, from_document, from_document_with_options, from_reader, from_reader_utf8_lossy, - from_slice, from_slice_utf8_lossy, Deserializer, DeserializerOptions, + from_bson, from_bson_with_options, from_document, from_document_with_options, from_reader, + from_reader_utf8_lossy, from_slice, from_slice_utf8_lossy, Deserializer, + DeserializerOptions, }, decimal128::Decimal128, raw::{ - RawArray, RawBinary, RawBson, RawDbPointer, RawDocument, RawDocumentBuf, RawJavaScriptCodeWithScope, - RawRegex, + RawBson, RawArray, RawArrayBuf, RawBinaryRef, RawBsonRef, RawDbPointerRef, RawDocument, + RawDocumentBuf, RawJavaScriptCodeWithScopeRef, RawRegexRef, }, ser::{ to_bson, to_bson_with_options, to_document, to_document_with_options, to_vec, Serializer, diff --git a/src/raw/array.rs b/src/raw/array.rs index 665c5632..22c04251 100644 --- a/src/raw/array.rs +++ b/src/raw/array.rs @@ -1,23 +1,25 @@ -use std::convert::TryFrom; +use std::{borrow::Cow, convert::TryFrom}; use serde::{ser::SerializeSeq, Deserialize, Serialize}; use super::{ error::{ValueAccessError, ValueAccessErrorKind, ValueAccessResult}, + serde::OwnedOrBorrowedRawArray, Error, Iter, - RawBinary, - RawBson, + RawBinaryRef, + RawBsonRef, RawDocument, - RawRegex, + RawRegexRef, Result, }; use crate::{ oid::ObjectId, - raw::{RawBsonVisitor, RAW_ARRAY_NEWTYPE}, - spec::{BinarySubtype, ElementType}, + raw::RAW_ARRAY_NEWTYPE, + spec::ElementType, Bson, DateTime, + RawArrayBuf, Timestamp, }; @@ -41,7 +43,7 @@ use crate::{ /// }; /// let bytes = bson::to_vec(&doc)?; /// -/// let rawdoc = RawDocument::new(bytes.as_slice())?; +/// let rawdoc = RawDocument::from_bytes(bytes.as_slice())?; /// let rawarray = rawdoc.get_array("x")?; /// /// for v in rawarray { @@ -64,7 +66,7 @@ use crate::{ /// }; /// let bytes = bson::to_vec(&doc)?; /// -/// let rawdoc = RawDocument::new(bytes.as_slice())?; +/// let rawdoc = RawDocument::from_bytes(bytes.as_slice())?; /// let rawarray = rawdoc.get_array("x")?; /// /// assert_eq!(rawarray.get_bool(1)?, true); @@ -90,8 +92,15 @@ impl RawArray { unsafe { &*(doc as *const RawDocument as *const RawArray) } } + /// Convert this borrowed [`RawArray`] into an owned [`RawArrayBuf`]. + /// + /// This involves a traversal of the array to count the values. + pub fn to_raw_array_buf(&self) -> RawArrayBuf { + RawArrayBuf::from_raw_document_buf(self.doc.to_raw_document_buf()) + } + /// Gets a reference to the value at the given index. - pub fn get(&self, index: usize) -> Result>> { + pub fn get(&self, index: usize) -> Result>> { self.into_iter().nth(index).transpose() } @@ -99,7 +108,7 @@ impl RawArray { &'a self, index: usize, expected_type: ElementType, - f: impl FnOnce(RawBson<'a>) -> Option, + f: impl FnOnce(RawBsonRef<'a>) -> Option, ) -> ValueAccessResult { let bson = self .get(index) @@ -126,73 +135,77 @@ impl RawArray { /// Gets the BSON double at the given index or returns an error if the value at that index isn't /// a double. pub fn get_f64(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::Double, RawBson::as_f64) + self.get_with(index, ElementType::Double, RawBsonRef::as_f64) } /// Gets a reference to the string at the given index or returns an error if the /// value at that index isn't a string. pub fn get_str(&self, index: usize) -> ValueAccessResult<&str> { - self.get_with(index, ElementType::String, RawBson::as_str) + self.get_with(index, ElementType::String, RawBsonRef::as_str) } /// Gets a reference to the document at the given index or returns an error if the /// value at that index isn't a document. pub fn get_document(&self, index: usize) -> ValueAccessResult<&RawDocument> { - self.get_with(index, ElementType::EmbeddedDocument, RawBson::as_document) + self.get_with( + index, + ElementType::EmbeddedDocument, + RawBsonRef::as_document, + ) } /// Gets a reference to the array at the given index or returns an error if the /// value at that index isn't a array. pub fn get_array(&self, index: usize) -> ValueAccessResult<&RawArray> { - self.get_with(index, ElementType::Array, RawBson::as_array) + self.get_with(index, ElementType::Array, RawBsonRef::as_array) } /// Gets a reference to the BSON binary value at the given index or returns an error if the /// value at that index isn't a binary. - pub fn get_binary(&self, index: usize) -> ValueAccessResult> { - self.get_with(index, ElementType::Binary, RawBson::as_binary) + pub fn get_binary(&self, index: usize) -> ValueAccessResult> { + self.get_with(index, ElementType::Binary, RawBsonRef::as_binary) } /// Gets the ObjectId at the given index or returns an error if the value at that index isn't an /// ObjectId. pub fn get_object_id(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::ObjectId, RawBson::as_object_id) + self.get_with(index, ElementType::ObjectId, RawBsonRef::as_object_id) } /// Gets the boolean at the given index or returns an error if the value at that index isn't a /// boolean. pub fn get_bool(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::Boolean, RawBson::as_bool) + self.get_with(index, ElementType::Boolean, RawBsonRef::as_bool) } /// Gets the DateTime at the given index or returns an error if the value at that index isn't a /// DateTime. pub fn get_datetime(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::DateTime, RawBson::as_datetime) + self.get_with(index, ElementType::DateTime, RawBsonRef::as_datetime) } /// Gets a reference to the BSON regex at the given index or returns an error if the /// value at that index isn't a regex. - pub fn get_regex(&self, index: usize) -> ValueAccessResult> { - self.get_with(index, ElementType::RegularExpression, RawBson::as_regex) + pub fn get_regex(&self, index: usize) -> ValueAccessResult> { + self.get_with(index, ElementType::RegularExpression, RawBsonRef::as_regex) } /// Gets a reference to the BSON timestamp at the given index or returns an error if the /// value at that index isn't a timestamp. pub fn get_timestamp(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::Timestamp, RawBson::as_timestamp) + self.get_with(index, ElementType::Timestamp, RawBsonRef::as_timestamp) } /// Gets the BSON int32 at the given index or returns an error if the value at that index isn't /// a 32-bit integer. pub fn get_i32(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::Int32, RawBson::as_i32) + self.get_with(index, ElementType::Int32, RawBsonRef::as_i32) } /// Gets BSON int64 at the given index or returns an error if the value at that index isn't a /// 64-bit integer. pub fn get_i64(&self, index: usize) -> ValueAccessResult { - self.get_with(index, ElementType::Int64, RawBson::as_i64) + self.get_with(index, ElementType::Int64, RawBsonRef::as_i64) } /// Gets a reference to the raw bytes of the [`RawArray`]. @@ -222,9 +235,23 @@ impl TryFrom<&RawArray> for Vec { } } +impl ToOwned for RawArray { + type Owned = RawArrayBuf; + + fn to_owned(&self) -> Self::Owned { + self.to_raw_array_buf() + } +} + +impl<'a> From<&'a RawArray> for Cow<'a, RawArray> { + fn from(rdr: &'a RawArray) -> Self { + Cow::Borrowed(rdr) + } +} + impl<'a> IntoIterator for &'a RawArray { type IntoIter = RawArrayIter<'a>; - type Item = Result>; + type Item = Result>; fn into_iter(self) -> RawArrayIter<'a> { RawArrayIter { @@ -239,9 +266,9 @@ pub struct RawArrayIter<'a> { } impl<'a> Iterator for RawArrayIter<'a> { - type Item = Result>; + type Item = Result>; - fn next(&mut self) -> Option>> { + fn next(&mut self) -> Option>> { match self.inner.next() { Some(Ok((_, v))) => Some(Ok(v)), Some(Err(e)) => Some(Err(e)), @@ -255,15 +282,11 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a RawArray { where D: serde::Deserializer<'de>, { - match deserializer.deserialize_newtype_struct(RAW_ARRAY_NEWTYPE, RawBsonVisitor)? { - RawBson::Array(d) => Ok(d), - RawBson::Binary(b) if b.subtype == BinarySubtype::Generic => { - let doc = RawDocument::new(b.bytes).map_err(serde::de::Error::custom)?; - Ok(RawArray::from_doc(doc)) - } - b => Err(serde::de::Error::custom(format!( - "expected raw array reference, instead got {:?}", - b + match OwnedOrBorrowedRawArray::deserialize(deserializer)? { + OwnedOrBorrowedRawArray::Borrowed(b) => Ok(b), + o => Err(serde::de::Error::custom(format!( + "expected borrowed raw array, instead got owned {:?}", + o ))), } } diff --git a/src/raw/array_buf.rs b/src/raw/array_buf.rs new file mode 100644 index 00000000..3c389d1f --- /dev/null +++ b/src/raw/array_buf.rs @@ -0,0 +1,187 @@ +use std::{ + borrow::{Borrow, Cow}, + fmt::Debug, + iter::FromIterator, +}; + +use serde::{Deserialize, Serialize}; + +use crate::{RawArray, RawBsonRef, RawDocumentBuf}; + +use super::{bson::RawBson, serde::OwnedOrBorrowedRawArray, RawArrayIter}; + +/// An owned BSON array value (akin to [`std::path::PathBuf`]), backed by a buffer of raw BSON +/// bytes. This type can be used to construct owned array values, which can be used to append to +/// [`RawDocumentBuf`] or as a field in a `Deserialize` struct. +/// +/// Iterating over a [`RawArrayBuf`] yields either an error or a [`RawBson`] value that borrows from +/// the original document without making any additional allocations. +/// ``` +/// # use bson::raw::Error; +/// use bson::raw::RawArrayBuf; +/// +/// let mut array = RawArrayBuf::new(); +/// array.push("a string"); +/// array.push(12_i32); +/// +/// let mut iter = array.into_iter(); +/// +/// let value = iter.next().unwrap()?; +/// assert_eq!(value.as_str(), Some("a string")); +/// +/// let value = iter.next().unwrap()?; +/// assert_eq!(value.as_i32(), Some(12)); +/// +/// assert!(iter.next().is_none()); +/// # Ok::<(), Error>(()) +/// ``` +/// +/// This type implements `Deref` to [`RawArray`], meaning that all methods on [`RawArray`] are +/// available on [`RawArrayBuf`] values as well. This includes [`RawArray::get`] or any of the +/// type-specific getters, such as [`RawArray::get_object_id`] or [`RawArray::get_str`]. Note +/// that accessing elements is an O(N) operation, as it requires iterating through the document from +/// the beginning to find the requested key. +#[derive(Clone, PartialEq)] +pub struct RawArrayBuf { + inner: RawDocumentBuf, + len: usize, +} + +impl RawArrayBuf { + /// Construct a new, empty `RawArrayBuf`. + pub fn new() -> RawArrayBuf { + Self { + inner: RawDocumentBuf::new(), + len: 0, + } + } + + /// Construct a new `RawArrayBuf` from the provided `Vec` of bytes. + /// + /// This involves a traversal of the array to count the values. + pub(crate) fn from_raw_document_buf(doc: RawDocumentBuf) -> Self { + let len = doc.iter().count(); + Self { inner: doc, len } + } + + /// Append a value to the end of the array. + /// + /// ``` + /// # use bson::raw::Error; + /// use bson::raw::{RawArrayBuf, RawDocumentBuf}; + /// + /// let mut array = RawArrayBuf::new(); + /// array.push("a string"); + /// array.push(12_i32); + /// + /// let mut doc = RawDocumentBuf::new(); + /// doc.append("a key", "a value"); + /// array.push(doc.clone()); + /// + /// let mut iter = array.into_iter(); + /// + /// let value = iter.next().unwrap()?; + /// assert_eq!(value.as_str(), Some("a string")); + /// + /// let value = iter.next().unwrap()?; + /// assert_eq!(value.as_i32(), Some(12)); + /// + /// let value = iter.next().unwrap()?; + /// assert_eq!(value.as_document(), Some(doc.as_ref())); + /// + /// assert!(iter.next().is_none()); + /// # Ok::<(), Error>(()) + /// ``` + pub fn push(&mut self, value: impl Into) { + self.inner.append(self.len.to_string(), value); + self.len += 1; + } + + pub(crate) fn into_vec(self) -> Vec { + self.inner.into_bytes() + } +} + +impl Debug for RawArrayBuf { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RawArrayBuf") + .field("data", &hex::encode(self.as_bytes())) + .field("len", &self.len) + .finish() + } +} + +impl std::ops::Deref for RawArrayBuf { + type Target = RawArray; + + fn deref(&self) -> &Self::Target { + RawArray::from_doc(&self.inner) + } +} + +impl AsRef for RawArrayBuf { + fn as_ref(&self) -> &RawArray { + RawArray::from_doc(&self.inner) + } +} + +impl Borrow for RawArrayBuf { + fn borrow(&self) -> &RawArray { + self.as_ref() + } +} + +impl<'a> IntoIterator for &'a RawArrayBuf { + type IntoIter = RawArrayIter<'a>; + type Item = super::Result>; + + fn into_iter(self) -> RawArrayIter<'a> { + self.as_ref().into_iter() + } +} + +impl<'a> From for Cow<'a, RawArray> { + fn from(rd: RawArrayBuf) -> Self { + Cow::Owned(rd) + } +} + +impl<'a> From<&'a RawArrayBuf> for Cow<'a, RawArray> { + fn from(rd: &'a RawArrayBuf) -> Self { + Cow::Borrowed(rd.as_ref()) + } +} + +impl> FromIterator for RawArrayBuf { + fn from_iter>(iter: I) -> Self { + let mut array_buf = RawArrayBuf::new(); + for item in iter { + array_buf.push(item); + } + array_buf + } +} + +impl<'de> Deserialize<'de> for RawArrayBuf { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + Ok(OwnedOrBorrowedRawArray::deserialize(deserializer)?.into_owned()) + } +} + +impl Serialize for RawArrayBuf { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.as_ref().serialize(serializer) + } +} + +impl Default for RawArrayBuf { + fn default() -> Self { + Self::new() + } +} diff --git a/src/raw/bson.rs b/src/raw/bson.rs index 3402c11b..05f4dfc6 100644 --- a/src/raw/bson.rs +++ b/src/raw/bson.rs @@ -1,43 +1,55 @@ use std::convert::{TryFrom, TryInto}; -use serde::{de::Visitor, ser::SerializeStruct, Deserialize, Serialize}; -use serde_bytes::{ByteBuf, Bytes}; +use serde::{Deserialize, Serialize}; -use super::{Error, RawArray, RawDocument, Result}; use crate::{ - de::convert_unsigned_to_signed_raw, - extjson, oid::{self, ObjectId}, - raw::{RAW_ARRAY_NEWTYPE, RAW_BSON_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, - spec::{BinarySubtype, ElementType}, + raw::RAW_BSON_NEWTYPE, + spec::ElementType, + Binary, Bson, - DateTime, DbPointer, Decimal128, + RawArray, + RawArrayBuf, + RawBinaryRef, + RawBsonRef, + RawDbPointerRef, + RawDocument, + RawDocumentBuf, + RawJavaScriptCodeWithScopeRef, + RawRegexRef, + Regex, Timestamp, }; -/// A BSON value referencing raw bytes stored elsewhere. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum RawBson<'a> { +use super::{ + serde::{OwnedOrBorrowedRawBson, OwnedOrBorrowedRawBsonVisitor}, + Error, + Result, +}; + +/// A BSON value backed by owned raw BSON bytes. +#[derive(Debug, Clone, PartialEq)] +pub enum RawBson { /// 64-bit binary floating point Double(f64), /// UTF-8 string - String(&'a str), + String(String), /// Array - Array(&'a RawArray), + Array(RawArrayBuf), /// Embedded document - Document(&'a RawDocument), + Document(RawDocumentBuf), /// Boolean value Boolean(bool), /// Null value Null, /// Regular expression - RegularExpression(RawRegex<'a>), + RegularExpression(Regex), /// JavaScript code - JavaScriptCode(&'a str), + JavaScriptCode(String), /// JavaScript code w/ scope - JavaScriptCodeWithScope(RawJavaScriptCodeWithScope<'a>), + JavaScriptCodeWithScope(RawJavaScriptCodeWithScope), /// 32-bit signed integer Int32(i32), /// 64-bit signed integer @@ -45,13 +57,13 @@ pub enum RawBson<'a> { /// Timestamp Timestamp(Timestamp), /// Binary data - Binary(RawBinary<'a>), + Binary(Binary), /// [ObjectId](http://dochub.mongodb.org/core/objectids) ObjectId(oid::ObjectId), /// UTC datetime DateTime(crate::DateTime), /// Symbol (Deprecated) - Symbol(&'a str), + Symbol(String), /// [128-bit decimal floating point](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst) Decimal128(Decimal128), /// Undefined value (Deprecated) @@ -61,10 +73,10 @@ pub enum RawBson<'a> { /// Min key MinKey, /// DBPointer (Deprecated) - DbPointer(RawDbPointer<'a>), + DbPointer(DbPointer), } -impl<'a> RawBson<'a> { +impl RawBson { /// Get the [`ElementType`] of this value. pub fn element_type(&self) -> ElementType { match *self { @@ -92,511 +104,374 @@ impl<'a> RawBson<'a> { } } - /// Gets the `f64` that's referenced or returns `None` if the referenced value isn't a BSON + /// Gets the wrapped `f64` value or returns `None` if the value isn't a BSON /// double. - pub fn as_f64(self) -> Option { + pub fn as_f64(&self) -> Option { match self { - RawBson::Double(d) => Some(d), + RawBson::Double(d) => Some(*d), _ => None, } } - /// Gets the `&str` that's referenced or returns `None` if the referenced value isn't a BSON - /// String. - pub fn as_str(self) -> Option<&'a str> { + /// Gets a reference to the `String` that's wrapped or returns `None` if the wrapped value isn't + /// a BSON String. + pub fn as_str(&self) -> Option<&'_ str> { match self { RawBson::String(s) => Some(s), _ => None, } } - /// Gets the [`RawArray`] that's referenced or returns `None` if the referenced value - /// isn't a BSON array. - pub fn as_array(self) -> Option<&'a RawArray> { + /// Gets a reference to the [`RawArrayBuf`] that's wrapped or returns `None` if the wrapped + /// value isn't a BSON array. + pub fn as_array(&self) -> Option<&'_ RawArray> { match self { RawBson::Array(v) => Some(v), _ => None, } } - /// Gets the [`RawDocument`] that's referenced or returns `None` if the referenced value - /// isn't a BSON document. - pub fn as_document(self) -> Option<&'a RawDocument> { + /// Gets a mutable reference to the [`RawArrayBuf`] that's wrapped or returns `None` if the + /// wrapped value isn't a BSON array. + pub fn as_array_mut(&mut self) -> Option<&mut RawArrayBuf> { + match self { + RawBson::Array(ref mut v) => Some(v), + _ => None, + } + } + + /// Gets a reference to the [`RawDocumentBuf`] that's wrapped or returns `None` if the wrapped + /// value isn't a BSON document. + pub fn as_document(&self) -> Option<&'_ RawDocument> { match self { RawBson::Document(v) => Some(v), _ => None, } } - /// Gets the `bool` that's referenced or returns `None` if the referenced value isn't a BSON + /// Gets a mutable reference to the [`RawDocumentBuf`] that's wrapped or returns `None` if the + /// wrapped value isn't a BSON document. + pub fn as_document_mut(&mut self) -> Option<&mut RawDocumentBuf> { + match self { + RawBson::Document(ref mut v) => Some(v), + _ => None, + } + } + + /// Gets the wrapped `bool` value or returns `None` if the wrapped value isn't a BSON /// boolean. - pub fn as_bool(self) -> Option { + pub fn as_bool(&self) -> Option { match self { - RawBson::Boolean(v) => Some(v), + RawBson::Boolean(v) => Some(*v), _ => None, } } - /// Gets the `i32` that's referenced or returns `None` if the referenced value isn't a BSON + /// Gets the wrapped `i32` value or returns `None` if the wrapped value isn't a BSON /// Int32. - pub fn as_i32(self) -> Option { + pub fn as_i32(&self) -> Option { match self { - RawBson::Int32(v) => Some(v), + RawBson::Int32(v) => Some(*v), _ => None, } } - /// Gets the `i64` that's referenced or returns `None` if the referenced value isn't a BSON + /// Gets the wrapped `i64` value or returns `None` if the wrapped value isn't a BSON /// Int64. - pub fn as_i64(self) -> Option { + pub fn as_i64(&self) -> Option { match self { - RawBson::Int64(v) => Some(v), + RawBson::Int64(v) => Some(*v), _ => None, } } - /// Gets the [`crate::oid::ObjectId`] that's referenced or returns `None` if the referenced - /// value isn't a BSON ObjectID. - pub fn as_object_id(self) -> Option { + /// Gets the wrapped [`crate::oid::ObjectId`] value or returns `None` if the wrapped value isn't + /// a BSON ObjectID. + pub fn as_object_id(&self) -> Option { match self { - RawBson::ObjectId(v) => Some(v), + RawBson::ObjectId(v) => Some(*v), _ => None, } } - /// Gets the [`RawBinary`] that's referenced or returns `None` if the referenced value isn't a - /// BSON binary. - pub fn as_binary(self) -> Option> { + /// Gets a reference to the [`Binary`] that's wrapped or returns `None` if the wrapped value + /// isn't a BSON binary. + pub fn as_binary(&self) -> Option> { match self { - RawBson::Binary(v) => Some(v), + RawBson::Binary(v) => Some(RawBinaryRef { + bytes: v.bytes.as_slice(), + subtype: v.subtype, + }), _ => None, } } - /// Gets the [`RawRegex`] that's referenced or returns `None` if the referenced value isn't a - /// BSON regular expression. - pub fn as_regex(self) -> Option> { + /// Gets a reference to the [`Regex`] that's wrapped or returns `None` if the wrapped value + /// isn't a BSON regular expression. + pub fn as_regex(&self) -> Option> { match self { - RawBson::RegularExpression(v) => Some(v), + RawBson::RegularExpression(v) => Some(RawRegexRef { + pattern: v.pattern.as_str(), + options: v.options.as_str(), + }), _ => None, } } - /// Gets the [`crate::DateTime`] that's referenced or returns `None` if the referenced value - /// isn't a BSON datetime. - pub fn as_datetime(self) -> Option { + /// Gets the wrapped [`crate::DateTime`] value or returns `None` if the wrapped value isn't a + /// BSON datetime. + pub fn as_datetime(&self) -> Option { match self { - RawBson::DateTime(v) => Some(v), + RawBson::DateTime(v) => Some(*v), _ => None, } } - /// Gets the symbol that's referenced or returns `None` if the referenced value isn't a BSON - /// symbol. - pub fn as_symbol(self) -> Option<&'a str> { + /// Gets a reference to the symbol that's wrapped or returns `None` if the wrapped value isn't a + /// BSON Symbol. + pub fn as_symbol(&self) -> Option<&'_ str> { match self { RawBson::Symbol(v) => Some(v), _ => None, } } - /// Gets the [`crate::Timestamp`] that's referenced or returns `None` if the referenced value - /// isn't a BSON timestamp. - pub fn as_timestamp(self) -> Option { + /// Gets the wrapped [`crate::Timestamp`] value or returns `None` if the wrapped value isn't a + /// BSON datetime. + pub fn as_timestamp(&self) -> Option { match self { - RawBson::Timestamp(timestamp) => Some(timestamp), + RawBson::Timestamp(timestamp) => Some(*timestamp), _ => None, } } - /// Gets the null value that's referenced or returns `None` if the referenced value isn't a BSON - /// null. - pub fn as_null(self) -> Option<()> { + /// Returns `Some(())` if this value is null, otherwise returns `None`. + pub fn as_null(&self) -> Option<()> { match self { RawBson::Null => Some(()), _ => None, } } - /// Gets the [`RawDbPointer`] that's referenced or returns `None` if the referenced value isn't - /// a BSON DB pointer. - pub fn as_db_pointer(self) -> Option> { + /// Gets a reference to the [`crate::DbPointer`] that's wrapped or returns `None` if the wrapped + /// value isn't a BSON DbPointer. + pub fn as_db_pointer(&self) -> Option> { match self { - RawBson::DbPointer(d) => Some(d), + RawBson::DbPointer(d) => Some(RawDbPointerRef { + namespace: d.namespace.as_str(), + id: d.id, + }), _ => None, } } - /// Gets the code that's referenced or returns `None` if the referenced value isn't a BSON - /// JavaScript. - pub fn as_javascript(self) -> Option<&'a str> { + /// Gets a reference to the code that's wrapped or returns `None` if the wrapped value isn't a + /// BSON JavaScript code. + pub fn as_javascript(&self) -> Option<&'_ str> { match self { RawBson::JavaScriptCode(s) => Some(s), _ => None, } } - /// Gets the [`RawJavaScriptCodeWithScope`] that's referenced or returns `None` if the - /// referenced value isn't a BSON JavaScript with scope. - pub fn as_javascript_with_scope(self) -> Option> { + /// Gets a reference to the [`RawJavaScriptCodeWithScope`] that's wrapped or returns `None` + /// if the wrapped value isn't a BSON JavaScript code with scope value. + pub fn as_javascript_with_scope(&self) -> Option> { match self { - RawBson::JavaScriptCodeWithScope(s) => Some(s), + RawBson::JavaScriptCodeWithScope(s) => Some(RawJavaScriptCodeWithScopeRef { + code: s.code.as_str(), + scope: &s.scope, + }), _ => None, } } -} - -/// A visitor used to deserialize types backed by raw BSON. -pub(crate) struct RawBsonVisitor; -impl<'de> Visitor<'de> for RawBsonVisitor { - type Value = RawBson<'de>; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "a raw BSON reference") + /// Gets a [`RawBsonRef`] value referencing this owned raw BSON value. + pub fn as_raw_bson_ref(&self) -> RawBsonRef<'_> { + match self { + RawBson::Double(d) => RawBsonRef::Double(*d), + RawBson::String(s) => RawBsonRef::String(s.as_str()), + RawBson::Array(a) => RawBsonRef::Array(a), + RawBson::Document(d) => RawBsonRef::Document(d), + RawBson::Boolean(b) => RawBsonRef::Boolean(*b), + RawBson::Null => RawBsonRef::Null, + RawBson::RegularExpression(re) => RawBsonRef::RegularExpression(RawRegexRef { + options: re.options.as_str(), + pattern: re.pattern.as_str(), + }), + RawBson::JavaScriptCode(c) => RawBsonRef::JavaScriptCode(c.as_str()), + RawBson::JavaScriptCodeWithScope(code_w_scope) => { + RawBsonRef::JavaScriptCodeWithScope(RawJavaScriptCodeWithScopeRef { + code: code_w_scope.code.as_str(), + scope: code_w_scope.scope.as_ref(), + }) + } + RawBson::Int32(i) => RawBsonRef::Int32(*i), + RawBson::Int64(i) => RawBsonRef::Int64(*i), + RawBson::Timestamp(ts) => RawBsonRef::Timestamp(*ts), + RawBson::Binary(b) => RawBsonRef::Binary(RawBinaryRef { + bytes: b.bytes.as_slice(), + subtype: b.subtype, + }), + RawBson::ObjectId(oid) => RawBsonRef::ObjectId(*oid), + RawBson::DateTime(dt) => RawBsonRef::DateTime(*dt), + RawBson::Symbol(s) => RawBsonRef::Symbol(s.as_str()), + RawBson::Decimal128(d) => RawBsonRef::Decimal128(*d), + RawBson::Undefined => RawBsonRef::Undefined, + RawBson::MaxKey => RawBsonRef::MaxKey, + RawBson::MinKey => RawBsonRef::MinKey, + RawBson::DbPointer(dbp) => RawBsonRef::DbPointer(RawDbPointerRef { + namespace: dbp.namespace.as_str(), + id: dbp.id, + }), + } } +} - fn visit_borrowed_str(self, v: &'de str) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::String(v)) +impl From for RawBson { + fn from(i: i32) -> Self { + RawBson::Int32(i) } +} - fn visit_borrowed_bytes(self, bytes: &'de [u8]) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Binary(RawBinary { - bytes, - subtype: BinarySubtype::Generic, - })) +impl From for RawBson { + fn from(i: i64) -> Self { + RawBson::Int64(i) } +} - fn visit_i8(self, v: i8) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Int32(v.into())) +impl From for RawBson { + fn from(s: String) -> Self { + RawBson::String(s) } +} - fn visit_i16(self, v: i16) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Int32(v.into())) +impl From<&str> for RawBson { + fn from(s: &str) -> Self { + RawBson::String(s.to_owned()) } +} - fn visit_i32(self, v: i32) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Int32(v)) +impl From for RawBson { + fn from(f: f64) -> Self { + RawBson::Double(f) } +} - fn visit_i64(self, v: i64) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Int64(v)) +impl From for RawBson { + fn from(b: bool) -> Self { + RawBson::Boolean(b) } +} - fn visit_u8(self, value: u8) -> std::result::Result - where - E: serde::de::Error, - { - convert_unsigned_to_signed_raw(value.into()) +impl From for RawBson { + fn from(d: RawDocumentBuf) -> Self { + RawBson::Document(d) } +} - fn visit_u16(self, value: u16) -> std::result::Result - where - E: serde::de::Error, - { - convert_unsigned_to_signed_raw(value.into()) +impl From for RawBson { + fn from(a: RawArrayBuf) -> Self { + RawBson::Array(a) } +} - fn visit_u32(self, value: u32) -> std::result::Result - where - E: serde::de::Error, - { - convert_unsigned_to_signed_raw(value.into()) +impl From for RawBson { + fn from(dt: crate::DateTime) -> Self { + RawBson::DateTime(dt) } +} - fn visit_u64(self, value: u64) -> std::result::Result - where - E: serde::de::Error, - { - convert_unsigned_to_signed_raw(value) +impl From for RawBson { + fn from(ts: Timestamp) -> Self { + RawBson::Timestamp(ts) } +} - fn visit_bool(self, v: bool) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Boolean(v)) +impl From for RawBson { + fn from(oid: ObjectId) -> Self { + RawBson::ObjectId(oid) } +} - fn visit_f64(self, v: f64) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Double(v)) +impl From for RawBson { + fn from(d: Decimal128) -> Self { + RawBson::Decimal128(d) } +} - fn visit_none(self) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Null) +impl From for RawBson { + fn from(code_w_scope: RawJavaScriptCodeWithScope) -> Self { + RawBson::JavaScriptCodeWithScope(code_w_scope) } +} - fn visit_unit(self) -> std::result::Result - where - E: serde::de::Error, - { - Ok(RawBson::Null) +impl From for RawBson { + fn from(b: Binary) -> Self { + RawBson::Binary(b) } +} - fn visit_newtype_struct(self, deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_any(self) +impl From for RawBson { + fn from(re: Regex) -> Self { + RawBson::RegularExpression(re) } +} - fn visit_map(self, mut map: A) -> std::result::Result - where - A: serde::de::MapAccess<'de>, - { - let k = map - .next_key::<&str>()? - .ok_or_else(|| serde::de::Error::custom("expected a key when deserializing RawBson"))?; - match k { - "$oid" => { - let oid: ObjectId = map.next_value()?; - Ok(RawBson::ObjectId(oid)) - } - "$symbol" => { - let s: &str = map.next_value()?; - Ok(RawBson::Symbol(s)) - } - "$numberDecimalBytes" => { - let bytes = map.next_value::()?; - return Ok(RawBson::Decimal128(Decimal128::deserialize_from_slice( - &bytes, - )?)); - } - "$regularExpression" => { - #[derive(Debug, Deserialize)] - struct BorrowedRegexBody<'a> { - pattern: &'a str, - - options: &'a str, - } - let body: BorrowedRegexBody = map.next_value()?; - Ok(RawBson::RegularExpression(RawRegex { - pattern: body.pattern, - options: body.options, - })) - } - "$undefined" => { - let _: bool = map.next_value()?; - Ok(RawBson::Undefined) - } - "$binary" => { - #[derive(Debug, Deserialize)] - struct BorrowedBinaryBody<'a> { - bytes: &'a [u8], - - #[serde(rename = "subType")] - subtype: u8, - } - - let v = map.next_value::()?; - - Ok(RawBson::Binary(RawBinary { - bytes: v.bytes, - subtype: v.subtype.into(), - })) - } - "$date" => { - let v = map.next_value::()?; - Ok(RawBson::DateTime(DateTime::from_millis(v))) - } - "$timestamp" => { - let v = map.next_value::()?; - Ok(RawBson::Timestamp(Timestamp { - time: v.t, - increment: v.i, - })) - } - "$minKey" => { - let _ = map.next_value::()?; - Ok(RawBson::MinKey) - } - "$maxKey" => { - let _ = map.next_value::()?; - Ok(RawBson::MaxKey) - } - "$code" => { - let code = map.next_value::<&str>()?; - if let Some(key) = map.next_key::<&str>()? { - if key == "$scope" { - let scope = map.next_value::<&RawDocument>()?; - Ok(RawBson::JavaScriptCodeWithScope( - RawJavaScriptCodeWithScope { code, scope }, - )) - } else { - Err(serde::de::Error::unknown_field(key, &["$scope"])) - } - } else { - Ok(RawBson::JavaScriptCode(code)) - } - } - "$dbPointer" => { - #[derive(Deserialize)] - struct BorrowedDbPointerBody<'a> { - #[serde(rename = "$ref")] - ns: &'a str, - - #[serde(rename = "$id")] - id: ObjectId, - } - - let body: BorrowedDbPointerBody = map.next_value()?; - Ok(RawBson::DbPointer(RawDbPointer { - namespace: body.ns, - id: body.id, - })) - } - RAW_DOCUMENT_NEWTYPE => { - let bson = map.next_value::<&[u8]>()?; - let doc = RawDocument::new(bson).map_err(serde::de::Error::custom)?; - Ok(RawBson::Document(doc)) - } - RAW_ARRAY_NEWTYPE => { - let bson = map.next_value::<&[u8]>()?; - let doc = RawDocument::new(bson).map_err(serde::de::Error::custom)?; - Ok(RawBson::Array(RawArray::from_doc(doc))) - } - k => Err(serde::de::Error::custom(format!( - "can't deserialize RawBson from map, key={}", - k - ))), - } +impl From for RawBson { + fn from(d: DbPointer) -> Self { + RawBson::DbPointer(d) } } -impl<'de: 'a, 'a> Deserialize<'de> for RawBson<'a> { +impl<'de> Deserialize<'de> for RawBson { fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_newtype_struct(RAW_BSON_NEWTYPE, RawBsonVisitor) + match deserializer + .deserialize_newtype_struct(RAW_BSON_NEWTYPE, OwnedOrBorrowedRawBsonVisitor)? + { + OwnedOrBorrowedRawBson::Owned(o) => Ok(o), + OwnedOrBorrowedRawBson::Borrowed(b) => Ok(b.to_raw_bson()), + } } } -impl<'a> Serialize for RawBson<'a> { +impl Serialize for RawBson { fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { - match self { - RawBson::Double(v) => serializer.serialize_f64(*v), - RawBson::String(v) => serializer.serialize_str(v), - RawBson::Array(v) => v.serialize(serializer), - RawBson::Document(v) => v.serialize(serializer), - RawBson::Boolean(v) => serializer.serialize_bool(*v), - RawBson::Null => serializer.serialize_unit(), - RawBson::Int32(v) => serializer.serialize_i32(*v), - RawBson::Int64(v) => serializer.serialize_i64(*v), - RawBson::ObjectId(oid) => oid.serialize(serializer), - RawBson::DateTime(dt) => dt.serialize(serializer), - RawBson::Binary(b) => b.serialize(serializer), - RawBson::JavaScriptCode(c) => { - let mut state = serializer.serialize_struct("$code", 1)?; - state.serialize_field("$code", c)?; - state.end() - } - RawBson::JavaScriptCodeWithScope(code_w_scope) => code_w_scope.serialize(serializer), - RawBson::DbPointer(dbp) => dbp.serialize(serializer), - RawBson::Symbol(s) => { - let mut state = serializer.serialize_struct("$symbol", 1)?; - state.serialize_field("$symbol", s)?; - state.end() - } - RawBson::RegularExpression(re) => re.serialize(serializer), - RawBson::Timestamp(t) => t.serialize(serializer), - RawBson::Decimal128(d) => d.serialize(serializer), - RawBson::Undefined => { - let mut state = serializer.serialize_struct("$undefined", 1)?; - state.serialize_field("$undefined", &true)?; - state.end() - } - RawBson::MaxKey => { - let mut state = serializer.serialize_struct("$maxKey", 1)?; - state.serialize_field("$maxKey", &1)?; - state.end() - } - RawBson::MinKey => { - let mut state = serializer.serialize_struct("$minKey", 1)?; - state.serialize_field("$minKey", &1)?; - state.end() - } - } + self.as_raw_bson_ref().serialize(serializer) } } -impl<'a> TryFrom> for Bson { +impl<'a> TryFrom for Bson { type Error = Error; - fn try_from(rawbson: RawBson<'a>) -> Result { + fn try_from(rawbson: RawBson) -> Result { Ok(match rawbson { RawBson::Double(d) => Bson::Double(d), - RawBson::String(s) => Bson::String(s.to_string()), - RawBson::Document(rawdoc) => { - let doc = rawdoc.try_into()?; - Bson::Document(doc) - } - RawBson::Array(rawarray) => { - let mut items = Vec::new(); - for v in rawarray { - let bson: Bson = v?.try_into()?; - items.push(bson); - } - Bson::Array(items) - } - RawBson::Binary(rawbson) => { - let RawBinary { - subtype, - bytes: data, - } = rawbson; - Bson::Binary(crate::Binary { - subtype, - bytes: data.to_vec(), - }) - } + RawBson::String(s) => Bson::String(s), + RawBson::Document(rawdoc) => Bson::Document(rawdoc.as_ref().try_into()?), + RawBson::Array(rawarray) => Bson::Array(rawarray.as_ref().try_into()?), + RawBson::Binary(rawbson) => Bson::Binary(rawbson), RawBson::ObjectId(rawbson) => Bson::ObjectId(rawbson), RawBson::Boolean(rawbson) => Bson::Boolean(rawbson), RawBson::DateTime(rawbson) => Bson::DateTime(rawbson), RawBson::Null => Bson::Null, - RawBson::RegularExpression(rawregex) => Bson::RegularExpression(crate::Regex::new( - rawregex.pattern.to_string(), - rawregex.options.to_string(), - )), - RawBson::JavaScriptCode(rawbson) => Bson::JavaScriptCode(rawbson.to_string()), + RawBson::RegularExpression(rawregex) => Bson::RegularExpression(rawregex), + RawBson::JavaScriptCode(rawbson) => Bson::JavaScriptCode(rawbson), RawBson::Int32(rawbson) => Bson::Int32(rawbson), RawBson::Timestamp(rawbson) => Bson::Timestamp(rawbson), RawBson::Int64(rawbson) => Bson::Int64(rawbson), RawBson::Undefined => Bson::Undefined, - RawBson::DbPointer(rawbson) => Bson::DbPointer(DbPointer { - namespace: rawbson.namespace.to_string(), - id: rawbson.id, - }), - RawBson::Symbol(rawbson) => Bson::Symbol(rawbson.to_string()), + RawBson::DbPointer(rawbson) => Bson::DbPointer(rawbson), + RawBson::Symbol(rawbson) => Bson::Symbol(rawbson), RawBson::JavaScriptCodeWithScope(rawbson) => { Bson::JavaScriptCodeWithScope(crate::JavaScriptCodeWithScope { - code: rawbson.code.to_string(), + code: rawbson.code, scope: rawbson.scope.try_into()?, }) } @@ -607,142 +482,17 @@ impl<'a> TryFrom> for Bson { } } -/// A BSON binary value referencing raw bytes stored elsewhere. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct RawBinary<'a> { - /// The subtype of the binary value. - pub subtype: BinarySubtype, - - /// The binary bytes. - pub bytes: &'a [u8], -} - -impl<'de: 'a, 'a> Deserialize<'de> for RawBinary<'a> { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - match RawBson::deserialize(deserializer)? { - RawBson::Binary(b) => Ok(b), - c => Err(serde::de::Error::custom(format!( - "expected binary, but got {:?} instead", - c - ))), - } - } -} - -impl<'a> Serialize for RawBinary<'a> { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - if let BinarySubtype::Generic = self.subtype { - serializer.serialize_bytes(self.bytes) - } else if !serializer.is_human_readable() { - #[derive(Serialize)] - struct BorrowedBinary<'a> { - bytes: &'a Bytes, - - #[serde(rename = "subType")] - subtype: u8, - } - - let mut state = serializer.serialize_struct("$binary", 1)?; - let body = BorrowedBinary { - bytes: Bytes::new(self.bytes), - subtype: self.subtype.into(), - }; - state.serialize_field("$binary", &body)?; - state.end() - } else { - let mut state = serializer.serialize_struct("$binary", 1)?; - let body = extjson::models::BinaryBody { - base64: base64::encode(self.bytes), - subtype: hex::encode([self.subtype.into()]), - }; - state.serialize_field("$binary", &body)?; - state.end() - } - } -} - -/// A BSON regex referencing raw bytes stored elsewhere. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct RawRegex<'a> { - pub(crate) pattern: &'a str, - pub(crate) options: &'a str, -} - -impl<'a> RawRegex<'a> { - /// Gets the pattern portion of the regex. - pub fn pattern(self) -> &'a str { - self.pattern - } +/// A BSON "code with scope" value backed by owned raw BSON. +#[derive(Debug, Clone, PartialEq)] +pub struct RawJavaScriptCodeWithScope { + /// The code value. + pub code: String, - /// Gets the options portion of the regex. - pub fn options(self) -> &'a str { - self.options - } + /// The scope document. + pub scope: RawDocumentBuf, } -impl<'de: 'a, 'a> Deserialize<'de> for RawRegex<'a> { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - match RawBson::deserialize(deserializer)? { - RawBson::RegularExpression(b) => Ok(b), - c => Err(serde::de::Error::custom(format!( - "expected Regex, but got {:?} instead", - c - ))), - } - } -} - -impl<'a> Serialize for RawRegex<'a> { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - #[derive(Serialize)] - struct BorrowedRegexBody<'a> { - pattern: &'a str, - options: &'a str, - } - - let mut state = serializer.serialize_struct("$regularExpression", 1)?; - let body = BorrowedRegexBody { - pattern: self.pattern, - options: self.options, - }; - state.serialize_field("$regularExpression", &body)?; - state.end() - } -} - -/// A BSON "code with scope" value referencing raw bytes stored elsewhere. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct RawJavaScriptCodeWithScope<'a> { - pub(crate) code: &'a str, - - pub(crate) scope: &'a RawDocument, -} - -impl<'a> RawJavaScriptCodeWithScope<'a> { - /// Gets the code in the value. - pub fn code(self) -> &'a str { - self.code - } - - /// Gets the scope in the value. - pub fn scope(self) -> &'a RawDocument { - self.scope - } -} - -impl<'de: 'a, 'a> Deserialize<'de> for RawJavaScriptCodeWithScope<'a> { +impl<'de> Deserialize<'de> for RawJavaScriptCodeWithScope { fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, @@ -757,60 +507,16 @@ impl<'de: 'a, 'a> Deserialize<'de> for RawJavaScriptCodeWithScope<'a> { } } -impl<'a> Serialize for RawJavaScriptCodeWithScope<'a> { +impl Serialize for RawJavaScriptCodeWithScope { fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { - let mut state = serializer.serialize_struct("$codeWithScope", 2)?; - state.serialize_field("$code", &self.code)?; - state.serialize_field("$scope", &self.scope)?; - state.end() - } -} - -/// A BSON DB pointer value referencing raw bytes stored elesewhere. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct RawDbPointer<'a> { - pub(crate) namespace: &'a str, - pub(crate) id: ObjectId, -} - -impl<'de: 'a, 'a> Deserialize<'de> for RawDbPointer<'a> { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - match RawBson::deserialize(deserializer)? { - RawBson::DbPointer(b) => Ok(b), - c => Err(serde::de::Error::custom(format!( - "expected DbPointer, but got {:?} instead", - c - ))), - } - } -} - -impl<'a> Serialize for RawDbPointer<'a> { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - #[derive(Serialize)] - struct BorrowedDbPointerBody<'a> { - #[serde(rename = "$ref")] - ref_ns: &'a str, - - #[serde(rename = "$id")] - id: ObjectId, - } - - let mut state = serializer.serialize_struct("$dbPointer", 1)?; - let body = BorrowedDbPointerBody { - ref_ns: self.namespace, - id: self.id, + let raw = RawJavaScriptCodeWithScopeRef { + code: self.code.as_str(), + scope: self.scope.as_ref(), }; - state.serialize_field("$dbPointer", &body)?; - state.end() + + raw.serialize(serializer) } } diff --git a/src/raw/bson_ref.rs b/src/raw/bson_ref.rs new file mode 100644 index 00000000..c12405d3 --- /dev/null +++ b/src/raw/bson_ref.rs @@ -0,0 +1,695 @@ +use std::convert::{TryFrom, TryInto}; + +use serde::{ser::SerializeStruct, Deserialize, Serialize}; +use serde_bytes::Bytes; + +use super::{ + bson::RawBson, + serde::{OwnedOrBorrowedRawBson, OwnedOrBorrowedRawBsonVisitor}, + Error, + RawArray, + RawDocument, + Result, +}; +use crate::{ + extjson, + oid::{self, ObjectId}, + raw::{RawJavaScriptCodeWithScope, RAW_BSON_NEWTYPE}, + spec::{BinarySubtype, ElementType}, + Binary, + Bson, + DbPointer, + Decimal128, + RawArrayBuf, + RawDocumentBuf, + Regex, + Timestamp, +}; + +/// A BSON value referencing raw bytes stored elsewhere. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum RawBsonRef<'a> { + /// 64-bit binary floating point + Double(f64), + /// UTF-8 string + String(&'a str), + /// Array + Array(&'a RawArray), + /// Embedded document + Document(&'a RawDocument), + /// Boolean value + Boolean(bool), + /// Null value + Null, + /// Regular expression + RegularExpression(RawRegexRef<'a>), + /// JavaScript code + JavaScriptCode(&'a str), + /// JavaScript code w/ scope + JavaScriptCodeWithScope(RawJavaScriptCodeWithScopeRef<'a>), + /// 32-bit signed integer + Int32(i32), + /// 64-bit signed integer + Int64(i64), + /// Timestamp + Timestamp(Timestamp), + /// Binary data + Binary(RawBinaryRef<'a>), + /// [ObjectId](http://dochub.mongodb.org/core/objectids) + ObjectId(oid::ObjectId), + /// UTC datetime + DateTime(crate::DateTime), + /// Symbol (Deprecated) + Symbol(&'a str), + /// [128-bit decimal floating point](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst) + Decimal128(Decimal128), + /// Undefined value (Deprecated) + Undefined, + /// Max key + MaxKey, + /// Min key + MinKey, + /// DBPointer (Deprecated) + DbPointer(RawDbPointerRef<'a>), +} + +impl<'a> RawBsonRef<'a> { + /// Get the [`ElementType`] of this value. + pub fn element_type(&self) -> ElementType { + match *self { + RawBsonRef::Double(..) => ElementType::Double, + RawBsonRef::String(..) => ElementType::String, + RawBsonRef::Array(..) => ElementType::Array, + RawBsonRef::Document(..) => ElementType::EmbeddedDocument, + RawBsonRef::Boolean(..) => ElementType::Boolean, + RawBsonRef::Null => ElementType::Null, + RawBsonRef::RegularExpression(..) => ElementType::RegularExpression, + RawBsonRef::JavaScriptCode(..) => ElementType::JavaScriptCode, + RawBsonRef::JavaScriptCodeWithScope(..) => ElementType::JavaScriptCodeWithScope, + RawBsonRef::Int32(..) => ElementType::Int32, + RawBsonRef::Int64(..) => ElementType::Int64, + RawBsonRef::Timestamp(..) => ElementType::Timestamp, + RawBsonRef::Binary(..) => ElementType::Binary, + RawBsonRef::ObjectId(..) => ElementType::ObjectId, + RawBsonRef::DateTime(..) => ElementType::DateTime, + RawBsonRef::Symbol(..) => ElementType::Symbol, + RawBsonRef::Decimal128(..) => ElementType::Decimal128, + RawBsonRef::Undefined => ElementType::Undefined, + RawBsonRef::MaxKey => ElementType::MaxKey, + RawBsonRef::MinKey => ElementType::MinKey, + RawBsonRef::DbPointer(..) => ElementType::DbPointer, + } + } + + /// Gets the `f64` that's referenced or returns `None` if the referenced value isn't a BSON + /// double. + pub fn as_f64(self) -> Option { + match self { + RawBsonRef::Double(d) => Some(d), + _ => None, + } + } + + /// Gets the `&str` that's referenced or returns `None` if the referenced value isn't a BSON + /// String. + pub fn as_str(self) -> Option<&'a str> { + match self { + RawBsonRef::String(s) => Some(s), + _ => None, + } + } + + /// Gets the [`RawArray`] that's referenced or returns `None` if the referenced value + /// isn't a BSON array. + pub fn as_array(self) -> Option<&'a RawArray> { + match self { + RawBsonRef::Array(v) => Some(v), + _ => None, + } + } + + /// Gets the [`RawDocument`] that's referenced or returns `None` if the referenced value + /// isn't a BSON document. + pub fn as_document(self) -> Option<&'a RawDocument> { + match self { + RawBsonRef::Document(v) => Some(v), + _ => None, + } + } + + /// Gets the `bool` that's referenced or returns `None` if the referenced value isn't a BSON + /// boolean. + pub fn as_bool(self) -> Option { + match self { + RawBsonRef::Boolean(v) => Some(v), + _ => None, + } + } + + /// Gets the `i32` that's referenced or returns `None` if the referenced value isn't a BSON + /// Int32. + pub fn as_i32(self) -> Option { + match self { + RawBsonRef::Int32(v) => Some(v), + _ => None, + } + } + + /// Gets the `i64` that's referenced or returns `None` if the referenced value isn't a BSON + /// Int64. + pub fn as_i64(self) -> Option { + match self { + RawBsonRef::Int64(v) => Some(v), + _ => None, + } + } + + /// Gets the [`crate::oid::ObjectId`] that's referenced or returns `None` if the referenced + /// value isn't a BSON ObjectID. + pub fn as_object_id(self) -> Option { + match self { + RawBsonRef::ObjectId(v) => Some(v), + _ => None, + } + } + + /// Gets the [`RawBinaryRef`] that's referenced or returns `None` if the referenced value isn't + /// a BSON binary. + pub fn as_binary(self) -> Option> { + match self { + RawBsonRef::Binary(v) => Some(v), + _ => None, + } + } + + /// Gets the [`RawRegexRef`] that's referenced or returns `None` if the referenced value isn't a + /// BSON regular expression. + pub fn as_regex(self) -> Option> { + match self { + RawBsonRef::RegularExpression(v) => Some(v), + _ => None, + } + } + + /// Gets the [`crate::DateTime`] that's referenced or returns `None` if the referenced value + /// isn't a BSON datetime. + pub fn as_datetime(self) -> Option { + match self { + RawBsonRef::DateTime(v) => Some(v), + _ => None, + } + } + + /// Gets the symbol that's referenced or returns `None` if the referenced value isn't a BSON + /// symbol. + pub fn as_symbol(self) -> Option<&'a str> { + match self { + RawBsonRef::Symbol(v) => Some(v), + _ => None, + } + } + + /// Gets the [`crate::Timestamp`] that's referenced or returns `None` if the referenced value + /// isn't a BSON timestamp. + pub fn as_timestamp(self) -> Option { + match self { + RawBsonRef::Timestamp(timestamp) => Some(timestamp), + _ => None, + } + } + + /// Gets the null value that's referenced or returns `None` if the referenced value isn't a BSON + /// null. + pub fn as_null(self) -> Option<()> { + match self { + RawBsonRef::Null => Some(()), + _ => None, + } + } + + /// Gets the [`RawDbPointerRef`] that's referenced or returns `None` if the referenced value + /// isn't a BSON DB pointer. + pub fn as_db_pointer(self) -> Option> { + match self { + RawBsonRef::DbPointer(d) => Some(d), + _ => None, + } + } + + /// Gets the code that's referenced or returns `None` if the referenced value isn't a BSON + /// JavaScript. + pub fn as_javascript(self) -> Option<&'a str> { + match self { + RawBsonRef::JavaScriptCode(s) => Some(s), + _ => None, + } + } + + /// Gets the [`RawJavaScriptCodeWithScope`] that's referenced or returns `None` if the + /// referenced value isn't a BSON JavaScript with scope. + pub fn as_javascript_with_scope(self) -> Option> { + match self { + RawBsonRef::JavaScriptCodeWithScope(s) => Some(s), + _ => None, + } + } + + /// Convert this [`RawBsonRef`] to the equivalent [`RawBson`]. + pub fn to_raw_bson(self) -> RawBson { + match self { + RawBsonRef::Double(d) => RawBson::Double(d), + RawBsonRef::String(s) => RawBson::String(s.to_string()), + RawBsonRef::Array(a) => RawBson::Array(a.to_owned()), + RawBsonRef::Document(d) => RawBson::Document(d.to_owned()), + RawBsonRef::Boolean(b) => RawBson::Boolean(b), + RawBsonRef::Null => RawBson::Null, + RawBsonRef::RegularExpression(re) => { + RawBson::RegularExpression(Regex::new(re.pattern, re.options)) + } + RawBsonRef::JavaScriptCode(c) => RawBson::JavaScriptCode(c.to_owned()), + RawBsonRef::JavaScriptCodeWithScope(c_w_s) => { + RawBson::JavaScriptCodeWithScope(RawJavaScriptCodeWithScope { + code: c_w_s.code.to_string(), + scope: c_w_s.scope.to_owned(), + }) + } + RawBsonRef::Int32(i) => RawBson::Int32(i), + RawBsonRef::Int64(i) => RawBson::Int64(i), + RawBsonRef::Timestamp(t) => RawBson::Timestamp(t), + RawBsonRef::Binary(b) => RawBson::Binary(Binary { + bytes: b.bytes.to_vec(), + subtype: b.subtype, + }), + RawBsonRef::ObjectId(o) => RawBson::ObjectId(o), + RawBsonRef::DateTime(dt) => RawBson::DateTime(dt), + RawBsonRef::Symbol(s) => RawBson::Symbol(s.to_string()), + RawBsonRef::Decimal128(d) => RawBson::Decimal128(d), + RawBsonRef::Undefined => RawBson::Undefined, + RawBsonRef::MaxKey => RawBson::MaxKey, + RawBsonRef::MinKey => RawBson::MinKey, + RawBsonRef::DbPointer(d) => RawBson::DbPointer(DbPointer { + namespace: d.namespace.to_string(), + id: d.id, + }), + } + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for RawBsonRef<'a> { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + match deserializer + .deserialize_newtype_struct(RAW_BSON_NEWTYPE, OwnedOrBorrowedRawBsonVisitor)? + { + OwnedOrBorrowedRawBson::Borrowed(b) => Ok(b), + o => Err(serde::de::Error::custom(format!( + "RawBson must be deserialized from borrowed content, instead got {:?}", + o + ))), + } + } +} + +impl<'a> Serialize for RawBsonRef<'a> { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + match self { + RawBsonRef::Double(v) => serializer.serialize_f64(*v), + RawBsonRef::String(v) => serializer.serialize_str(v), + RawBsonRef::Array(v) => v.serialize(serializer), + RawBsonRef::Document(v) => v.serialize(serializer), + RawBsonRef::Boolean(v) => serializer.serialize_bool(*v), + RawBsonRef::Null => serializer.serialize_unit(), + RawBsonRef::Int32(v) => serializer.serialize_i32(*v), + RawBsonRef::Int64(v) => serializer.serialize_i64(*v), + RawBsonRef::ObjectId(oid) => oid.serialize(serializer), + RawBsonRef::DateTime(dt) => dt.serialize(serializer), + RawBsonRef::Binary(b) => b.serialize(serializer), + RawBsonRef::JavaScriptCode(c) => { + let mut state = serializer.serialize_struct("$code", 1)?; + state.serialize_field("$code", c)?; + state.end() + } + RawBsonRef::JavaScriptCodeWithScope(code_w_scope) => code_w_scope.serialize(serializer), + RawBsonRef::DbPointer(dbp) => dbp.serialize(serializer), + RawBsonRef::Symbol(s) => { + let mut state = serializer.serialize_struct("$symbol", 1)?; + state.serialize_field("$symbol", s)?; + state.end() + } + RawBsonRef::RegularExpression(re) => re.serialize(serializer), + RawBsonRef::Timestamp(t) => t.serialize(serializer), + RawBsonRef::Decimal128(d) => d.serialize(serializer), + RawBsonRef::Undefined => { + let mut state = serializer.serialize_struct("$undefined", 1)?; + state.serialize_field("$undefined", &true)?; + state.end() + } + RawBsonRef::MaxKey => { + let mut state = serializer.serialize_struct("$maxKey", 1)?; + state.serialize_field("$maxKey", &1)?; + state.end() + } + RawBsonRef::MinKey => { + let mut state = serializer.serialize_struct("$minKey", 1)?; + state.serialize_field("$minKey", &1)?; + state.end() + } + } + } +} + +impl<'a> TryFrom> for Bson { + type Error = Error; + + fn try_from(rawbson: RawBsonRef<'a>) -> Result { + rawbson.to_raw_bson().try_into() + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(i: i32) -> Self { + RawBsonRef::Int32(i) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(i: i64) -> Self { + RawBsonRef::Int64(i) + } +} + +impl<'a> From<&'a str> for RawBsonRef<'a> { + fn from(s: &'a str) -> Self { + RawBsonRef::String(s) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(f: f64) -> Self { + RawBsonRef::Double(f) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(b: bool) -> Self { + RawBsonRef::Boolean(b) + } +} + +impl<'a> From<&'a RawDocumentBuf> for RawBsonRef<'a> { + fn from(d: &'a RawDocumentBuf) -> Self { + RawBsonRef::Document(d.as_ref()) + } +} + +impl<'a> From<&'a RawDocument> for RawBsonRef<'a> { + fn from(d: &'a RawDocument) -> Self { + RawBsonRef::Document(d) + } +} + +impl<'a> From<&'a RawArray> for RawBsonRef<'a> { + fn from(a: &'a RawArray) -> Self { + RawBsonRef::Array(a) + } +} + +impl<'a> From<&'a RawArrayBuf> for RawBsonRef<'a> { + fn from(a: &'a RawArrayBuf) -> Self { + RawBsonRef::Array(a) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(dt: crate::DateTime) -> Self { + RawBsonRef::DateTime(dt) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(ts: Timestamp) -> Self { + RawBsonRef::Timestamp(ts) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(oid: ObjectId) -> Self { + RawBsonRef::ObjectId(oid) + } +} + +impl<'a> From for RawBsonRef<'a> { + fn from(d: Decimal128) -> Self { + RawBsonRef::Decimal128(d) + } +} + +/// A BSON binary value referencing raw bytes stored elsewhere. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct RawBinaryRef<'a> { + /// The subtype of the binary value. + pub subtype: BinarySubtype, + + /// The binary bytes. + pub bytes: &'a [u8], +} + +impl<'a> RawBinaryRef<'a> { + pub(crate) fn len(&self) -> i32 { + match self.subtype { + BinarySubtype::BinaryOld => self.bytes.len() as i32 + 4, + _ => self.bytes.len() as i32, + } + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for RawBinaryRef<'a> { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + match RawBsonRef::deserialize(deserializer)? { + RawBsonRef::Binary(b) => Ok(b), + c => Err(serde::de::Error::custom(format!( + "expected binary, but got {:?} instead", + c + ))), + } + } +} + +impl<'a> Serialize for RawBinaryRef<'a> { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + if let BinarySubtype::Generic = self.subtype { + serializer.serialize_bytes(self.bytes) + } else if !serializer.is_human_readable() { + #[derive(Serialize)] + struct BorrowedBinary<'a> { + bytes: &'a Bytes, + + #[serde(rename = "subType")] + subtype: u8, + } + + let mut state = serializer.serialize_struct("$binary", 1)?; + let body = BorrowedBinary { + bytes: Bytes::new(self.bytes), + subtype: self.subtype.into(), + }; + state.serialize_field("$binary", &body)?; + state.end() + } else { + let mut state = serializer.serialize_struct("$binary", 1)?; + let body = extjson::models::BinaryBody { + base64: base64::encode(self.bytes), + subtype: hex::encode([self.subtype.into()]), + }; + state.serialize_field("$binary", &body)?; + state.end() + } + } +} + +impl<'a> From> for RawBsonRef<'a> { + fn from(b: RawBinaryRef<'a>) -> Self { + RawBsonRef::Binary(b) + } +} + +impl<'a> From<&'a Binary> for RawBsonRef<'a> { + fn from(bin: &'a Binary) -> Self { + bin.as_raw_binary().into() + } +} + +/// A BSON regex referencing raw bytes stored elsewhere. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct RawRegexRef<'a> { + pub(crate) pattern: &'a str, + pub(crate) options: &'a str, +} + +impl<'a> RawRegexRef<'a> { + /// Gets the pattern portion of the regex. + pub fn pattern(self) -> &'a str { + self.pattern + } + + /// Gets the options portion of the regex. + pub fn options(self) -> &'a str { + self.options + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for RawRegexRef<'a> { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + match RawBsonRef::deserialize(deserializer)? { + RawBsonRef::RegularExpression(b) => Ok(b), + c => Err(serde::de::Error::custom(format!( + "expected Regex, but got {:?} instead", + c + ))), + } + } +} + +impl<'a> Serialize for RawRegexRef<'a> { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + #[derive(Serialize)] + struct BorrowedRegexBody<'a> { + pattern: &'a str, + options: &'a str, + } + + let mut state = serializer.serialize_struct("$regularExpression", 1)?; + let body = BorrowedRegexBody { + pattern: self.pattern, + options: self.options, + }; + state.serialize_field("$regularExpression", &body)?; + state.end() + } +} + +impl<'a> From> for RawBsonRef<'a> { + fn from(re: RawRegexRef<'a>) -> Self { + RawBsonRef::RegularExpression(re) + } +} + +/// A BSON "code with scope" value referencing raw bytes stored elsewhere. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct RawJavaScriptCodeWithScopeRef<'a> { + pub(crate) code: &'a str, + + pub(crate) scope: &'a RawDocument, +} + +impl<'a> RawJavaScriptCodeWithScopeRef<'a> { + /// Gets the code in the value. + pub fn code(self) -> &'a str { + self.code + } + + /// Gets the scope in the value. + pub fn scope(self) -> &'a RawDocument { + self.scope + } + + pub(crate) fn len(self) -> i32 { + 4 + 4 + self.code.len() as i32 + 1 + self.scope.as_bytes().len() as i32 + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for RawJavaScriptCodeWithScopeRef<'a> { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + match RawBsonRef::deserialize(deserializer)? { + RawBsonRef::JavaScriptCodeWithScope(b) => Ok(b), + c => Err(serde::de::Error::custom(format!( + "expected CodeWithScope, but got {:?} instead", + c + ))), + } + } +} + +impl<'a> Serialize for RawJavaScriptCodeWithScopeRef<'a> { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("$codeWithScope", 2)?; + state.serialize_field("$code", &self.code)?; + state.serialize_field("$scope", &self.scope)?; + state.end() + } +} + +impl<'a> From> for RawBsonRef<'a> { + fn from(code_w_scope: RawJavaScriptCodeWithScopeRef<'a>) -> Self { + RawBsonRef::JavaScriptCodeWithScope(code_w_scope) + } +} + +/// A BSON DB pointer value referencing raw bytes stored elesewhere. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct RawDbPointerRef<'a> { + pub(crate) namespace: &'a str, + pub(crate) id: ObjectId, +} + +impl<'de: 'a, 'a> Deserialize<'de> for RawDbPointerRef<'a> { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + match RawBsonRef::deserialize(deserializer)? { + RawBsonRef::DbPointer(b) => Ok(b), + c => Err(serde::de::Error::custom(format!( + "expected DbPointer, but got {:?} instead", + c + ))), + } + } +} + +impl<'a> Serialize for RawDbPointerRef<'a> { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + #[derive(Serialize)] + struct BorrowedDbPointerBody<'a> { + #[serde(rename = "$ref")] + ref_ns: &'a str, + + #[serde(rename = "$id")] + id: ObjectId, + } + + let mut state = serializer.serialize_struct("$dbPointer", 1)?; + let body = BorrowedDbPointerBody { + ref_ns: self.namespace, + id: self.id, + }; + state.serialize_field("$dbPointer", &body)?; + state.end() + } +} diff --git a/src/raw/document.rs b/src/raw/document.rs index 34c1ef45..3f3e4318 100644 --- a/src/raw/document.rs +++ b/src/raw/document.rs @@ -6,8 +6,7 @@ use std::{ use serde::{ser::SerializeMap, Deserialize, Serialize}; use crate::{ - raw::{error::ErrorKind, RawBsonVisitor, RAW_DOCUMENT_NEWTYPE}, - spec::BinarySubtype, + raw::{error::ErrorKind, serde::OwnedOrBorrowedRawDocument, RAW_DOCUMENT_NEWTYPE}, DateTime, Timestamp, }; @@ -18,10 +17,10 @@ use super::{ Error, Iter, RawArray, - RawBinary, - RawBson, + RawBinaryRef, + RawBsonRef, RawDocumentBuf, - RawRegex, + RawRegexRef, Result, }; use crate::{oid::ObjectId, spec::ElementType, Document}; @@ -43,7 +42,7 @@ use crate::{oid::ObjectId, spec::ElementType, Document}; /// # use bson::raw::{Error}; /// use bson::raw::RawDocument; /// -/// let doc = RawDocument::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00")?; +/// let doc = RawDocument::from_bytes(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"); @@ -60,7 +59,7 @@ use crate::{oid::ObjectId, spec::ElementType, Document}; /// ``` /// use bson::raw::RawDocument; /// -/// let doc = RawDocument::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00")?; +/// let doc = RawDocument::from_bytes(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00")?; /// assert_eq!(doc.get_str("hi")?, "y'all"); /// # Ok::<(), Box>(()) /// ``` @@ -86,10 +85,10 @@ impl RawDocument { /// ``` /// use bson::raw::RawDocument; /// - /// let doc = RawDocument::new(b"\x05\0\0\0\0")?; + /// let doc = RawDocument::from_bytes(b"\x05\0\0\0\0")?; /// # Ok::<(), bson::raw::Error>(()) /// ``` - pub fn new + ?Sized>(data: &D) -> Result<&RawDocument> { + pub fn from_bytes + ?Sized>(data: &D) -> Result<&RawDocument> { let data = data.as_ref(); if data.len() < 5 { @@ -144,12 +143,12 @@ impl RawDocument { /// use bson::raw::{RawDocument, RawDocumentBuf, Error}; /// /// let data = b"\x05\0\0\0\0"; - /// let doc_ref = RawDocument::new(data)?; + /// let doc_ref = RawDocument::from_bytes(data)?; /// let doc: RawDocumentBuf = doc_ref.to_raw_document_buf(); /// # Ok::<(), Error>(()) pub fn to_raw_document_buf(&self) -> RawDocumentBuf { // unwrap is ok here because we already verified the bytes in `RawDocumentRef::new` - RawDocumentBuf::new(self.data.to_owned()).unwrap() + RawDocumentBuf::from_bytes(self.data.to_owned()).unwrap() } /// Gets a reference to the value corresponding to the given key by iterating until the key is @@ -169,7 +168,7 @@ impl RawDocument { /// assert!(doc.get("unknown")?.is_none()); /// # Ok::<(), Error>(()) /// ``` - pub fn get(&self, key: impl AsRef) -> Result>> { + pub fn get(&self, key: impl AsRef) -> Result>> { for result in self.into_iter() { let (k, v) = result?; if key.as_ref() == k { @@ -183,7 +182,7 @@ impl RawDocument { &'a self, key: impl AsRef, expected_type: ElementType, - f: impl FnOnce(RawBson<'a>) -> Option, + f: impl FnOnce(RawBsonRef<'a>) -> Option, ) -> ValueAccessResult { let key = key.as_ref(); @@ -228,7 +227,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_f64(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::Double, RawBson::as_f64) + self.get_with(key, ElementType::Double, RawBsonRef::as_f64) } /// Gets a reference to the string value corresponding to a given key or returns an error if the @@ -248,7 +247,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_str(&self, key: impl AsRef) -> ValueAccessResult<&'_ str> { - self.get_with(key, ElementType::String, RawBson::as_str) + self.get_with(key, ElementType::String, RawBsonRef::as_str) } /// Gets a reference to the document value corresponding to a given key or returns an error if @@ -269,7 +268,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_document(&self, key: impl AsRef) -> ValueAccessResult<&'_ RawDocument> { - self.get_with(key, ElementType::EmbeddedDocument, RawBson::as_document) + self.get_with(key, ElementType::EmbeddedDocument, RawBsonRef::as_document) } /// Gets a reference to the array value corresponding to a given key or returns an error if @@ -293,7 +292,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_array(&self, key: impl AsRef) -> ValueAccessResult<&'_ RawArray> { - self.get_with(key, ElementType::Array, RawBson::as_array) + self.get_with(key, ElementType::Array, RawBsonRef::as_array) } /// Gets a reference to the BSON binary value corresponding to a given key or returns an error @@ -302,7 +301,7 @@ impl RawDocument { /// ``` /// use bson::{ /// doc, - /// raw::{ValueAccessErrorKind, RawDocumentBuf, RawBinary}, + /// raw::{ValueAccessErrorKind, RawDocumentBuf, RawBinaryRef}, /// spec::BinarySubtype, /// Binary, /// }; @@ -317,8 +316,8 @@ impl RawDocument { /// assert!(matches!(doc.get_binary("unknown").unwrap_err().kind, ValueAccessErrorKind::NotPresent)); /// # Ok::<(), Box>(()) /// ``` - pub fn get_binary(&self, key: impl AsRef) -> ValueAccessResult> { - self.get_with(key, ElementType::Binary, RawBson::as_binary) + pub fn get_binary(&self, key: impl AsRef) -> ValueAccessResult> { + self.get_with(key, ElementType::Binary, RawBsonRef::as_binary) } /// Gets a reference to the ObjectId value corresponding to a given key or returns an error if @@ -339,7 +338,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_object_id(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::ObjectId, RawBson::as_object_id) + self.get_with(key, ElementType::ObjectId, RawBsonRef::as_object_id) } /// Gets a reference to the boolean value corresponding to a given key or returns an error if @@ -360,7 +359,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_bool(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::Boolean, RawBson::as_bool) + self.get_with(key, ElementType::Boolean, RawBsonRef::as_bool) } /// Gets a reference to the BSON DateTime value corresponding to a given key or returns an @@ -382,7 +381,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_datetime(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::DateTime, RawBson::as_datetime) + self.get_with(key, ElementType::DateTime, RawBsonRef::as_datetime) } /// Gets a reference to the BSON regex value corresponding to a given key or returns an error if @@ -405,8 +404,8 @@ impl RawDocument { /// assert!(matches!(doc.get_regex("unknown").unwrap_err().kind, ValueAccessErrorKind::NotPresent)); /// # Ok::<(), Box>(()) /// ``` - pub fn get_regex(&self, key: impl AsRef) -> ValueAccessResult> { - self.get_with(key, ElementType::RegularExpression, RawBson::as_regex) + pub fn get_regex(&self, key: impl AsRef) -> ValueAccessResult> { + self.get_with(key, ElementType::RegularExpression, RawBsonRef::as_regex) } /// Gets a reference to the BSON timestamp value corresponding to a given key or returns an @@ -430,7 +429,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_timestamp(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::Timestamp, RawBson::as_timestamp) + self.get_with(key, ElementType::Timestamp, RawBsonRef::as_timestamp) } /// Gets a reference to the BSON int32 value corresponding to a given key or returns an error if @@ -451,7 +450,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_i32(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::Int32, RawBson::as_i32) + self.get_with(key, ElementType::Int32, RawBsonRef::as_i32) } /// Gets a reference to the BSON int64 value corresponding to a given key or returns an error if @@ -472,7 +471,7 @@ impl RawDocument { /// # Ok::<(), Box>(()) /// ``` pub fn get_i64(&self, key: impl AsRef) -> ValueAccessResult { - self.get_with(key, ElementType::Int64, RawBson::as_i64) + self.get_with(key, ElementType::Int64, RawBsonRef::as_i64) } /// Return a reference to the contained data as a `&[u8]` @@ -494,19 +493,11 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a RawDocument { where D: serde::Deserializer<'de>, { - match deserializer.deserialize_newtype_struct(RAW_DOCUMENT_NEWTYPE, RawBsonVisitor)? { - RawBson::Document(d) => Ok(d), - - // For non-BSON formats, RawDocument gets serialized as bytes, so we need to deserialize - // from them here too. For BSON, the deserializier will return an error if it - // sees the RAW_DOCUMENT_NEWTYPE but the next type isn't a document. - RawBson::Binary(b) if b.subtype == BinarySubtype::Generic => { - RawDocument::new(b.bytes).map_err(serde::de::Error::custom) - } - - o => Err(serde::de::Error::custom(format!( - "expected raw document reference, instead got {:?}", - o + match OwnedOrBorrowedRawDocument::deserialize(deserializer)? { + OwnedOrBorrowedRawDocument::Borrowed(b) => Ok(b), + OwnedOrBorrowedRawDocument::Owned(d) => Err(serde::de::Error::custom(format!( + "expected borrowed raw document, instead got owned {:?}", + d ))), } } @@ -581,7 +572,7 @@ impl TryFrom<&RawDocument> for crate::Document { impl<'a> IntoIterator for &'a RawDocument { type IntoIter = Iter<'a>; - type Item = Result<(&'a str, RawBson<'a>)>; + type Item = Result<(&'a str, RawBsonRef<'a>)>; fn into_iter(self) -> Iter<'a> { Iter::new(self) diff --git a/src/raw/document_buf.rs b/src/raw/document_buf.rs index f1c216d3..b7742522 100644 --- a/src/raw/document_buf.rs +++ b/src/raw/document_buf.rs @@ -1,14 +1,30 @@ use std::{ borrow::{Borrow, Cow}, - convert::TryFrom, + convert::{TryFrom, TryInto}, + iter::FromIterator, ops::Deref, }; use serde::{Deserialize, Serialize}; -use crate::Document; +use crate::{ + de::MIN_BSON_DOCUMENT_SIZE, + spec::BinarySubtype, + Document, + RawBinaryRef, + RawJavaScriptCodeWithScopeRef, +}; -use super::{Error, ErrorKind, Iter, RawBson, RawDocument, Result}; +use super::{ + bson::RawBson, + serde::OwnedOrBorrowedRawDocument, + Error, + ErrorKind, + Iter, + RawBsonRef, + RawDocument, + Result, +}; /// An owned BSON document (akin to [`std::path::PathBuf`]), backed by a buffer of raw BSON bytes. /// This can be created from a `Vec` or a [`crate::Document`]. @@ -24,7 +40,7 @@ use super::{Error, ErrorKind, Iter, RawBson, RawDocument, Result}; /// # use bson::raw::Error; /// use bson::raw::RawDocumentBuf; /// -/// let doc = RawDocumentBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; +/// let doc = RawDocumentBuf::from_bytes(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; /// let mut iter = doc.iter(); /// let (key, value) = iter.next().unwrap()?; /// assert_eq!(key, "hi"); @@ -42,7 +58,7 @@ use super::{Error, ErrorKind, Iter, RawBson, RawDocument, Result}; /// ``` /// use bson::raw::RawDocumentBuf; /// -/// let doc = RawDocumentBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; +/// let doc = RawDocumentBuf::from_bytes(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; /// assert_eq!(doc.get_str("hi")?, "y'all"); /// # Ok::<(), Box>(()) /// ``` @@ -52,6 +68,13 @@ pub struct RawDocumentBuf { } impl RawDocumentBuf { + /// Creates a new, empty [`RawDocumentBuf`]. + pub fn new() -> RawDocumentBuf { + let mut data: Vec = MIN_BSON_DOCUMENT_SIZE.to_le_bytes().to_vec(); + data.push(0); + Self { data } + } + /// Constructs a new [`RawDocumentBuf`], validating _only_ the /// following invariants: /// * `data` is at least five bytes long (the minimum for a valid BSON document) @@ -66,11 +89,11 @@ impl RawDocumentBuf { /// /// ``` /// # use bson::raw::{RawDocumentBuf, Error}; - /// let doc = RawDocumentBuf::new(b"\x05\0\0\0\0".to_vec())?; + /// let doc = RawDocumentBuf::from_bytes(b"\x05\0\0\0\0".to_vec())?; /// # Ok::<(), Error>(()) /// ``` - pub fn new(data: Vec) -> Result { - let _ = RawDocument::new(data.as_slice())?; + pub fn from_bytes(data: Vec) -> Result { + let _ = RawDocument::from_bytes(data.as_slice())?; Ok(Self { data }) } @@ -133,12 +156,161 @@ impl RawDocumentBuf { /// use bson::{doc, raw::RawDocumentBuf}; /// /// let doc = RawDocumentBuf::from_document(&doc!{})?; - /// assert_eq!(doc.into_vec(), b"\x05\x00\x00\x00\x00".to_vec()); + /// assert_eq!(doc.into_bytes(), b"\x05\x00\x00\x00\x00".to_vec()); /// # Ok::<(), Error>(()) /// ``` - pub fn into_vec(self) -> Vec { + pub fn into_bytes(self) -> Vec { self.data } + + /// Append a key value pair to the end of the document without checking to see if + /// the key already exists. + /// + /// It is a user error to append the same key more than once to the same document, and it may + /// result in errors when communicating with MongoDB. + /// + /// If the provided key contains an interior null byte, this method will panic. + /// + /// ``` + /// # use bson::raw::Error; + /// use bson::{doc, raw::RawDocumentBuf}; + /// + /// let mut doc = RawDocumentBuf::new(); + /// doc.append("a string", "some string"); + /// doc.append("an integer", 12_i32); + /// + /// let mut subdoc = RawDocumentBuf::new(); + /// subdoc.append("a key", true); + /// doc.append("a document", subdoc); + /// + /// let expected = doc! { + /// "a string": "some string", + /// "an integer": 12_i32, + /// "a document": { "a key": true }, + /// }; + /// + /// assert_eq!(doc.to_document()?, expected); + /// # Ok::<(), Error>(()) + /// ``` + pub fn append(&mut self, key: impl Into, value: impl Into) { + fn append_string(doc: &mut RawDocumentBuf, value: String) { + doc.data + .extend(&((value.as_bytes().len() + 1) as i32).to_le_bytes()); + doc.data.extend(value.into_bytes()); + doc.data.push(0); + } + + fn append_cstring(doc: &mut RawDocumentBuf, value: String) { + if value.contains('\0') { + panic!("cstr includes interior null byte: {}", value) + } + doc.data.extend(value.into_bytes()); + doc.data.push(0); + } + + let original_len = self.data.len(); + + // write the key for the next value to the end + // the element type will replace the previous null byte terminator of the document + append_cstring(self, key.into()); + + 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); + } + 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) => { + let byte = if b { 1 } else { 0 }; + self.data.push(byte); + } + RawBson::DateTime(dt) => { + self.data.extend(&dt.timestamp_millis().to_le_bytes()); + } + RawBson::DbPointer(dbp) => { + append_string(self, dbp.namespace); + 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); + append_cstring(self, re.options); + } + RawBson::JavaScriptCode(js) => { + append_string(self, js); + } + 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); + 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); + } + RawBson::Null | RawBson::Undefined | RawBson::MinKey | RawBson::MaxKey => {} + } + // update element type + self.data[original_len - 1] = element_type as u8; + // append trailing null byte + self.data.push(0); + // update length + self.data + .splice(0..4, (self.data.len() as i32).to_le_bytes().iter().cloned()); + } + + /// Convert this [`RawDocumentBuf`] to a [`Document`], returning an error + /// if invalid BSON is encountered. + pub fn to_document(&self) -> Result { + self.as_ref().try_into() + } +} + +impl Default for RawDocumentBuf { + fn default() -> Self { + Self::new() + } } impl<'de> Deserialize<'de> for RawDocumentBuf { @@ -146,9 +318,7 @@ impl<'de> Deserialize<'de> for RawDocumentBuf { where D: serde::Deserializer<'de>, { - // TODO: RUST-1045 implement visit_map to deserialize from arbitrary maps. - let doc: &'de RawDocument = Deserialize::deserialize(deserializer)?; - Ok(doc.to_owned()) + Ok(OwnedOrBorrowedRawDocument::deserialize(deserializer)?.into_owned()) } } @@ -192,7 +362,7 @@ impl TryFrom for Document { impl<'a> IntoIterator for &'a RawDocumentBuf { type IntoIter = Iter<'a>; - type Item = Result<(&'a str, RawBson<'a>)>; + type Item = Result<(&'a str, RawBsonRef<'a>)>; fn into_iter(self) -> Iter<'a> { Iter::new(self) @@ -218,3 +388,13 @@ impl Borrow for RawDocumentBuf { &*self } } + +impl, T: Into> FromIterator<(S, T)> for RawDocumentBuf { + fn from_iter>(iter: I) -> Self { + let mut buf = RawDocumentBuf::new(); + for (k, v) in iter { + buf.append(k, v); + } + buf + } +} diff --git a/src/raw/iter.rs b/src/raw/iter.rs index 2e46da52..97b1c94c 100644 --- a/src/raw/iter.rs +++ b/src/raw/iter.rs @@ -11,7 +11,7 @@ use crate::{ }; use super::{ - bson::RawDbPointer, + bson_ref::RawDbPointerRef, checked_add, error::try_with_key, f64_from_slice, @@ -20,11 +20,11 @@ use super::{ read_lenencoded, read_nullterminated, RawArray, - RawBinary, - RawBson, + RawBinaryRef, + RawBsonRef, RawDocument, - RawJavaScriptCodeWithScope, - RawRegex, + RawJavaScriptCodeWithScopeRef, + RawRegexRef, }; /// An iterator over the document's entries. @@ -91,14 +91,14 @@ impl<'a> Iter<'a> { }, }); } - RawDocument::new(&self.doc.as_bytes()[starting_at..end]) + RawDocument::from_bytes(&self.doc.as_bytes()[starting_at..end]) } } impl<'a> Iterator for Iter<'a> { - type Item = Result<(&'a str, RawBson<'a>)>; + type Item = Result<(&'a str, RawBsonRef<'a>)>; - fn next(&mut self) -> Option)>> { + fn next(&mut self) -> Option)>> { if !self.valid { return None; } else if self.offset == self.doc.as_bytes().len() - 1 { @@ -147,28 +147,28 @@ impl<'a> Iterator for Iter<'a> { let (element, element_size) = match element_type { ElementType::Int32 => { let i = i32_from_slice(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::Int32(i), 4) + (RawBsonRef::Int32(i), 4) } ElementType::Int64 => { let i = i64_from_slice(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::Int64(i), 8) + (RawBsonRef::Int64(i), 8) } ElementType::Double => { let f = f64_from_slice(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::Double(f), 8) + (RawBsonRef::Double(f), 8) } ElementType::String => { let s = read_lenencoded(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::String(s), 4 + s.len() + 1) + (RawBsonRef::String(s), 4 + s.len() + 1) } ElementType::EmbeddedDocument => { let doc = self.next_document(valueoffset)?; - (RawBson::Document(doc), doc.as_bytes().len()) + (RawBsonRef::Document(doc), doc.as_bytes().len()) } ElementType::Array => { let doc = self.next_document(valueoffset)?; ( - RawBson::Array(RawArray::from_doc(doc)), + RawBsonRef::Array(RawArray::from_doc(doc)), doc.as_bytes().len(), ) } @@ -198,7 +198,7 @@ impl<'a> Iterator for Iter<'a> { _ => &self.doc.as_bytes()[data_start..(data_start + len)], }; ( - RawBson::Binary(RawBinary { + RawBsonRef::Binary(RawBinaryRef { subtype, bytes: data, }), @@ -207,7 +207,7 @@ impl<'a> Iterator for Iter<'a> { } ElementType::ObjectId => { let oid = self.next_oid(valueoffset)?; - (RawBson::ObjectId(oid), 12) + (RawBsonRef::ObjectId(oid), 12) } ElementType::Boolean => { let b = read_bool(&self.doc.as_bytes()[valueoffset..]).map_err(|e| { @@ -218,11 +218,11 @@ impl<'a> Iterator for Iter<'a> { }, ) })?; - (RawBson::Boolean(b), 1) + (RawBsonRef::Boolean(b), 1) } ElementType::DateTime => { let ms = i64_from_slice(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::DateTime(DateTime::from_millis(ms)), 8) + (RawBsonRef::DateTime(DateTime::from_millis(ms)), 8) } ElementType::RegularExpression => { let pattern = read_nullterminated(&self.doc.as_bytes()[valueoffset..])?; @@ -230,12 +230,12 @@ impl<'a> Iterator for Iter<'a> { &self.doc.as_bytes()[(valueoffset + pattern.len() + 1)..], )?; ( - RawBson::RegularExpression(RawRegex { pattern, options }), + RawBsonRef::RegularExpression(RawRegexRef { pattern, options }), pattern.len() + 1 + options.len() + 1, ) } - ElementType::Null => (RawBson::Null, 0), - ElementType::Undefined => (RawBson::Undefined, 0), + ElementType::Null => (RawBsonRef::Null, 0), + ElementType::Undefined => (RawBsonRef::Undefined, 0), ElementType::Timestamp => { let ts = Timestamp::from_reader(&self.doc.as_bytes()[valueoffset..]).map_err( |e| { @@ -244,11 +244,11 @@ impl<'a> Iterator for Iter<'a> { }) }, )?; - (RawBson::Timestamp(ts), 8) + (RawBsonRef::Timestamp(ts), 8) } ElementType::JavaScriptCode => { let code = read_lenencoded(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::JavaScriptCode(code), 4 + code.len() + 1) + (RawBsonRef::JavaScriptCode(code), 4 + code.len() + 1) } ElementType::JavaScriptCodeWithScope => { let length = i32_from_slice(&self.doc.as_bytes()[valueoffset..])? as usize; @@ -263,9 +263,9 @@ impl<'a> Iterator for Iter<'a> { let slice = &&self.doc.as_bytes()[valueoffset..(valueoffset + length)]; let code = read_lenencoded(&slice[4..])?; let scope_start = 4 + 4 + code.len() + 1; - let scope = RawDocument::new(&slice[scope_start..])?; + let scope = RawDocument::from_bytes(&slice[scope_start..])?; ( - RawBson::JavaScriptCodeWithScope(RawJavaScriptCodeWithScope { + RawBsonRef::JavaScriptCodeWithScope(RawJavaScriptCodeWithScopeRef { code, scope, }), @@ -276,18 +276,18 @@ impl<'a> Iterator for Iter<'a> { let namespace = read_lenencoded(&self.doc.as_bytes()[valueoffset..])?; let id = self.next_oid(valueoffset + 4 + namespace.len() + 1)?; ( - RawBson::DbPointer(RawDbPointer { namespace, id }), + RawBsonRef::DbPointer(RawDbPointerRef { namespace, id }), 4 + namespace.len() + 1 + 12, ) } ElementType::Symbol => { let s = read_lenencoded(&self.doc.as_bytes()[valueoffset..])?; - (RawBson::Symbol(s), 4 + s.len() + 1) + (RawBsonRef::Symbol(s), 4 + s.len() + 1) } ElementType::Decimal128 => { self.verify_enough_bytes(valueoffset, 16)?; ( - RawBson::Decimal128(Decimal128::from_bytes( + RawBsonRef::Decimal128(Decimal128::from_bytes( self.doc.as_bytes()[valueoffset..(valueoffset + 16)] .try_into() .unwrap(), @@ -295,8 +295,8 @@ impl<'a> Iterator for Iter<'a> { 16, ) } - ElementType::MinKey => (RawBson::MinKey, 0), - ElementType::MaxKey => (RawBson::MaxKey, 0), + ElementType::MinKey => (RawBsonRef::MinKey, 0), + ElementType::MaxKey => (RawBsonRef::MaxKey, 0), }; self.offset = valueoffset + element_size; diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 89c16ea1..a85a8a92 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -23,7 +23,7 @@ //! }; //! //! // See http://bsonspec.org/spec.html for details on the binary encoding of BSON. -//! let doc = RawDocumentBuf::new(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; +//! let doc = RawDocumentBuf::from_bytes(b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00".to_vec())?; //! let elem = doc.get("hi")?.unwrap(); //! //! assert_eq!( @@ -76,7 +76,7 @@ //! use bson::raw::RawDocument; //! //! let bytes = b"\x13\x00\x00\x00\x02hi\x00\x06\x00\x00\x00y'all\x00\x00"; -//! assert_eq!(RawDocument::new(bytes)?.get_str("hi")?, "y'all"); +//! assert_eq!(RawDocument::from_bytes(bytes)?.get_str("hi")?, "y'all"); //! # Ok::<(), Box>(()) //! ``` //! @@ -88,7 +88,7 @@ //! ```rust //! use bson::{ //! raw::{ -//! RawBson, +//! RawBsonRef, //! RawDocumentBuf, //! }, //! doc, @@ -102,22 +102,25 @@ //! let doc = RawDocumentBuf::from_document(&original_doc)?; //! let mut doc_iter = doc.iter(); //! -//! let (key, value): (&str, RawBson) = doc_iter.next().unwrap()?; +//! let (key, value): (&str, RawBsonRef) = doc_iter.next().unwrap()?; //! assert_eq!(key, "crate"); //! assert_eq!(value.as_str(), Some("bson")); //! -//! let (key, value): (&str, RawBson) = doc_iter.next().unwrap()?; +//! let (key, value): (&str, RawBsonRef) = doc_iter.next().unwrap()?; //! assert_eq!(key, "year"); //! assert_eq!(value.as_str(), Some("2021")); //! # Ok::<(), bson::raw::Error>(()) //! ``` mod array; +mod array_buf; mod bson; +mod bson_ref; mod document; mod document_buf; mod error; mod iter; +mod serde; #[cfg(test)] mod test; @@ -127,15 +130,21 @@ use crate::de::MIN_BSON_STRING_SIZE; pub use self::{ array::{RawArray, RawArrayIter}, - bson::{RawBinary, RawBson, RawDbPointer, RawJavaScriptCodeWithScope, RawRegex}, + array_buf::RawArrayBuf, + bson::{RawBson, RawJavaScriptCodeWithScope}, + bson_ref::{ + RawBinaryRef, + RawBsonRef, + RawDbPointerRef, + RawJavaScriptCodeWithScopeRef, + RawRegexRef, + }, document::RawDocument, document_buf::RawDocumentBuf, error::{Error, ErrorKind, Result, ValueAccessError, ValueAccessErrorKind, ValueAccessResult}, iter::Iter, }; -pub(crate) use self::bson::RawBsonVisitor; - /// Special newtype name indicating that the type being (de)serialized is a raw BSON document. pub(crate) const RAW_DOCUMENT_NEWTYPE: &str = "$__private__bson_RawDocument"; diff --git a/src/raw/serde.rs b/src/raw/serde.rs new file mode 100644 index 00000000..246a422c --- /dev/null +++ b/src/raw/serde.rs @@ -0,0 +1,526 @@ +use std::{borrow::Cow, fmt::Debug}; + +use serde::{de::Visitor, Deserialize}; +use serde_bytes::ByteBuf; + +use crate::{ + de::convert_unsigned_to_signed_raw, + extjson, + oid::ObjectId, + raw::{RawJavaScriptCodeWithScope, RAW_ARRAY_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, + spec::BinarySubtype, + Binary, + DateTime, + DbPointer, + Decimal128, + RawArray, + RawArrayBuf, + RawBinaryRef, + RawBsonRef, + RawDbPointerRef, + RawDocument, + RawDocumentBuf, + RawJavaScriptCodeWithScopeRef, + RawRegexRef, + Regex, + Timestamp, +}; + +use super::{bson::RawBson, RAW_BSON_NEWTYPE}; + +/// A raw BSON value that may either be borrowed or owned. +/// +/// This is used to consolidate the `Serialize` and `Deserialize` implementations for +/// `RawBson` and `OwnedRawBson`. +pub(crate) enum OwnedOrBorrowedRawBson<'a> { + Owned(RawBson), + Borrowed(RawBsonRef<'a>), +} + +impl<'a, 'de: 'a> Deserialize<'de> for OwnedOrBorrowedRawBson<'a> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_newtype_struct(RAW_BSON_NEWTYPE, OwnedOrBorrowedRawBsonVisitor) + } +} + +impl<'a> Debug for OwnedOrBorrowedRawBson<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Owned(o) => o.fmt(f), + Self::Borrowed(b) => b.fmt(f), + } + } +} + +impl<'a> From> for OwnedOrBorrowedRawBson<'a> { + fn from(b: RawBsonRef<'a>) -> Self { + OwnedOrBorrowedRawBson::Borrowed(b) + } +} + +impl<'a> From for OwnedOrBorrowedRawBson<'a> { + fn from(b: RawBson) -> Self { + OwnedOrBorrowedRawBson::Owned(b) + } +} + +/// Wrapper around a `Cow` to enable borrowed deserialization. +/// The default `Deserialize` impl for `Cow` always uses the owned version. +#[derive(Debug, Deserialize)] +struct CowStr<'a>(#[serde(borrow)] Cow<'a, str>); + +/// Wrapper type that can deserialize either an owned or a borrowed raw BSON document. +#[derive(Debug)] +pub(crate) enum OwnedOrBorrowedRawDocument<'a> { + Owned(RawDocumentBuf), + Borrowed(&'a RawDocument), +} + +impl<'a> OwnedOrBorrowedRawDocument<'a> { + pub(crate) fn into_owned(self) -> RawDocumentBuf { + match self { + Self::Owned(o) => o, + Self::Borrowed(b) => b.to_owned(), + } + } +} + +impl<'a, 'de: 'a> Deserialize<'de> for OwnedOrBorrowedRawDocument<'a> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + match deserializer + .deserialize_newtype_struct(RAW_DOCUMENT_NEWTYPE, OwnedOrBorrowedRawBsonVisitor)? + { + OwnedOrBorrowedRawBson::Borrowed(RawBsonRef::Document(d)) => Ok(Self::Borrowed(d)), + OwnedOrBorrowedRawBson::Owned(RawBson::Document(d)) => Ok(Self::Owned(d)), + + // For non-BSON formats, RawDocument gets serialized as bytes, so we need to deserialize + // from them here too. For BSON, the deserializier will return an error if it + // sees the RAW_DOCUMENT_NEWTYPE but the next type isn't a document. + OwnedOrBorrowedRawBson::Borrowed(RawBsonRef::Binary(b)) + if b.subtype == BinarySubtype::Generic => + { + Ok(Self::Borrowed( + RawDocument::from_bytes(b.bytes).map_err(serde::de::Error::custom)?, + )) + } + OwnedOrBorrowedRawBson::Owned(RawBson::Binary(b)) + if b.subtype == BinarySubtype::Generic => + { + Ok(Self::Owned( + RawDocumentBuf::from_bytes(b.bytes).map_err(serde::de::Error::custom)?, + )) + } + + o => Err(serde::de::Error::custom(format!( + "expected raw document, instead got {:?}", + o + ))), + } + } +} + +/// Wrapper type that can deserialize either an owned or a borrowed raw BSON array. +#[derive(Debug)] +pub(crate) enum OwnedOrBorrowedRawArray<'a> { + Owned(RawArrayBuf), + Borrowed(&'a RawArray), +} + +impl<'a> OwnedOrBorrowedRawArray<'a> { + pub(crate) fn into_owned(self) -> RawArrayBuf { + match self { + Self::Owned(o) => o, + Self::Borrowed(b) => b.to_owned(), + } + } +} + +impl<'a, 'de: 'a> Deserialize<'de> for OwnedOrBorrowedRawArray<'a> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + match deserializer + .deserialize_newtype_struct(RAW_ARRAY_NEWTYPE, OwnedOrBorrowedRawBsonVisitor)? + { + OwnedOrBorrowedRawBson::Borrowed(RawBsonRef::Array(d)) => Ok(Self::Borrowed(d)), + OwnedOrBorrowedRawBson::Owned(RawBson::Array(d)) => Ok(Self::Owned(d)), + + // For non-BSON formats, RawArray gets serialized as bytes, so we need to deserialize + // from them here too. For BSON, the deserializier will return an error if it + // sees the RAW_DOCUMENT_NEWTYPE but the next type isn't a document. + OwnedOrBorrowedRawBson::Borrowed(RawBsonRef::Binary(b)) + if b.subtype == BinarySubtype::Generic => + { + let doc = RawDocument::from_bytes(b.bytes).map_err(serde::de::Error::custom)?; + Ok(Self::Borrowed(RawArray::from_doc(doc))) + } + OwnedOrBorrowedRawBson::Owned(RawBson::Binary(b)) + if b.subtype == BinarySubtype::Generic => + { + let doc = RawDocumentBuf::from_bytes(b.bytes).map_err(serde::de::Error::custom)?; + Ok(Self::Owned(RawArrayBuf::from_raw_document_buf(doc))) + } + + o => Err(serde::de::Error::custom(format!( + "expected raw array, instead got {:?}", + o + ))), + } + } +} + +/// A visitor used to deserialize types backed by raw BSON. +pub(crate) struct OwnedOrBorrowedRawBsonVisitor; + +impl<'de> Visitor<'de> for OwnedOrBorrowedRawBsonVisitor { + type Value = OwnedOrBorrowedRawBson<'de>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a raw BSON value") + } + + fn visit_borrowed_str(self, v: &'de str) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::String(v).into()) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(RawBson::String(v.to_string()).into()) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(RawBson::String(v).into()) + } + + fn visit_borrowed_bytes(self, bytes: &'de [u8]) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Binary(RawBinaryRef { + bytes, + subtype: BinarySubtype::Generic, + }) + .into()) + } + + fn visit_i8(self, v: i8) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Int32(v.into()).into()) + } + + fn visit_i16(self, v: i16) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Int32(v.into()).into()) + } + + fn visit_i32(self, v: i32) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Int32(v).into()) + } + + fn visit_i64(self, v: i64) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Int64(v).into()) + } + + fn visit_u8(self, value: u8) -> std::result::Result + where + E: serde::de::Error, + { + Ok(convert_unsigned_to_signed_raw(value.into())?.into()) + } + + fn visit_u16(self, value: u16) -> std::result::Result + where + E: serde::de::Error, + { + Ok(convert_unsigned_to_signed_raw(value.into())?.into()) + } + + fn visit_u32(self, value: u32) -> std::result::Result + where + E: serde::de::Error, + { + Ok(convert_unsigned_to_signed_raw(value.into())?.into()) + } + + fn visit_u64(self, value: u64) -> std::result::Result + where + E: serde::de::Error, + { + Ok(convert_unsigned_to_signed_raw(value)?.into()) + } + + fn visit_bool(self, v: bool) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Boolean(v).into()) + } + + fn visit_f64(self, v: f64) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Double(v).into()) + } + + fn visit_none(self) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Null.into()) + } + + fn visit_unit(self) -> std::result::Result + where + E: serde::de::Error, + { + Ok(RawBsonRef::Null.into()) + } + + fn visit_newtype_struct(self, deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_any(self) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: serde::de::Error, + { + Ok(RawBson::Binary(Binary { + bytes: v, + subtype: BinarySubtype::Generic, + }) + .into()) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut array = RawArrayBuf::new(); + while let Some(v) = seq.next_element::()? { + array.push(v); + } + Ok(RawBson::Array(array).into()) + } + + fn visit_map(self, mut map: A) -> std::result::Result + where + A: serde::de::MapAccess<'de>, + { + /// Helper function used to build up the rest of a document once we determine that + /// the map being visited isn't the serde data model version of a BSON type and is + /// in fact a regular map. + fn build_doc<'de, A>( + first_key: &str, + mut map: A, + ) -> std::result::Result, A::Error> + where + A: serde::de::MapAccess<'de>, + { + let mut doc = RawDocumentBuf::new(); + let v: RawBson = map.next_value()?; + doc.append(first_key, v); + + while let Some((k, v)) = map.next_entry::()? { + doc.append(k, v); + } + + Ok(RawBson::Document(doc).into()) + } + + let k = match map.next_key::()? { + Some(k) => k, + None => return Ok(RawBson::Document(RawDocumentBuf::new()).into()), + }; + + match k.0.as_ref() { + "$oid" => { + let oid: ObjectId = map.next_value()?; + Ok(RawBsonRef::ObjectId(oid).into()) + } + "$symbol" => { + let s: CowStr = map.next_value()?; + match s.0 { + Cow::Borrowed(s) => Ok(RawBsonRef::Symbol(s).into()), + Cow::Owned(s) => Ok(RawBson::Symbol(s).into()), + } + } + "$numberDecimalBytes" => { + let bytes = map.next_value::()?; + return Ok( + RawBsonRef::Decimal128(Decimal128::deserialize_from_slice(&bytes)?).into(), + ); + } + "$regularExpression" => { + #[derive(Debug, Deserialize)] + struct BorrowedRegexBody<'a> { + #[serde(borrow)] + pattern: Cow<'a, str>, + + #[serde(borrow)] + options: Cow<'a, str>, + } + let body: BorrowedRegexBody = map.next_value()?; + + match (body.pattern, body.options) { + (Cow::Borrowed(p), Cow::Borrowed(o)) => { + Ok(RawBsonRef::RegularExpression(RawRegexRef { + pattern: p, + options: o, + }) + .into()) + } + (p, o) => Ok(RawBson::RegularExpression(Regex { + pattern: p.into_owned(), + options: o.into_owned(), + }) + .into()), + } + } + "$undefined" => { + let _: bool = map.next_value()?; + Ok(RawBsonRef::Undefined.into()) + } + "$binary" => { + #[derive(Debug, Deserialize)] + struct BorrowedBinaryBody<'a> { + #[serde(borrow)] + bytes: Cow<'a, [u8]>, + + #[serde(rename = "subType")] + subtype: u8, + } + + let v = map.next_value::()?; + + if let Cow::Borrowed(bytes) = v.bytes { + Ok(RawBsonRef::Binary(RawBinaryRef { + bytes, + subtype: v.subtype.into(), + }) + .into()) + } else { + Ok(RawBson::Binary(Binary { + bytes: v.bytes.into_owned(), + subtype: v.subtype.into(), + }) + .into()) + } + } + "$date" => { + let v = map.next_value::()?; + Ok(RawBsonRef::DateTime(DateTime::from_millis(v)).into()) + } + "$timestamp" => { + let v = map.next_value::()?; + Ok(RawBsonRef::Timestamp(Timestamp { + time: v.t, + increment: v.i, + }) + .into()) + } + "$minKey" => { + let _ = map.next_value::()?; + Ok(RawBsonRef::MinKey.into()) + } + "$maxKey" => { + let _ = map.next_value::()?; + Ok(RawBsonRef::MaxKey.into()) + } + "$code" => { + let code = map.next_value::()?; + if let Some(key) = map.next_key::()? { + if key.0.as_ref() == "$scope" { + let scope = map.next_value::()?; + match (code.0, scope) { + (Cow::Borrowed(code), OwnedOrBorrowedRawDocument::Borrowed(scope)) => { + Ok(RawBsonRef::JavaScriptCodeWithScope( + RawJavaScriptCodeWithScopeRef { code, scope }, + ) + .into()) + } + (code, scope) => Ok(RawBson::JavaScriptCodeWithScope( + RawJavaScriptCodeWithScope { + code: code.into_owned(), + scope: scope.into_owned(), + }, + ) + .into()), + } + } else { + Err(serde::de::Error::unknown_field(&key.0, &["$scope"])) + } + } else if let Cow::Borrowed(code) = code.0 { + Ok(RawBsonRef::JavaScriptCode(code).into()) + } else { + Ok(RawBson::JavaScriptCode(code.0.into_owned()).into()) + } + } + "$dbPointer" => { + #[derive(Deserialize)] + struct BorrowedDbPointerBody<'a> { + #[serde(rename = "$ref")] + #[serde(borrow)] + ns: CowStr<'a>, + + #[serde(rename = "$id")] + id: ObjectId, + } + + let body: BorrowedDbPointerBody = map.next_value()?; + if let Cow::Borrowed(ns) = body.ns.0 { + Ok(RawBsonRef::DbPointer(RawDbPointerRef { + namespace: ns, + id: body.id, + }) + .into()) + } else { + Ok(RawBson::DbPointer(DbPointer { + namespace: body.ns.0.into_owned(), + id: body.id, + }) + .into()) + } + } + RAW_DOCUMENT_NEWTYPE => { + let bson = map.next_value::<&[u8]>()?; + let doc = RawDocument::from_bytes(bson).map_err(serde::de::Error::custom)?; + Ok(RawBsonRef::Document(doc).into()) + } + RAW_ARRAY_NEWTYPE => { + let bson = map.next_value::<&[u8]>()?; + let doc = RawDocument::from_bytes(bson).map_err(serde::de::Error::custom)?; + Ok(RawBsonRef::Array(RawArray::from_doc(doc)).into()) + } + k => build_doc(k, map), + } + } +} diff --git a/src/raw/test/append.rs b/src/raw/test/append.rs new file mode 100644 index 00000000..a8a0307b --- /dev/null +++ b/src/raw/test/append.rs @@ -0,0 +1,414 @@ +use std::iter::FromIterator; + +use crate::{ + oid::ObjectId, + raw::RawJavaScriptCodeWithScope, + spec::BinarySubtype, + tests::LOCK, + Binary, + Bson, + DateTime, + DbPointer, + Decimal128, + Document, + JavaScriptCodeWithScope, + RawArrayBuf, + RawBson, + RawDocumentBuf, + Regex, + Timestamp, +}; + +use pretty_assertions::assert_eq; + +fn append_test(expected: Document, append: impl FnOnce(&mut RawDocumentBuf)) { + let bytes = crate::to_vec(&expected).unwrap(); + let mut buf = RawDocumentBuf::new(); + append(&mut buf); + assert_eq!(buf.as_bytes(), bytes); +} + +#[test] +fn i32() { + let expected = doc! { + "a": -1_i32, + "b": 123_i32, + "c": 0_i32 + }; + append_test(expected, |doc| { + doc.append("a", -1_i32); + doc.append("b", 123_i32); + doc.append("c", 0_i32); + }); +} + +#[test] +fn i64() { + let expected = doc! { + "a": -1_i64, + "b": 123_i64, + "c": 0_i64 + }; + append_test(expected, |doc| { + doc.append("a", -1_i64); + doc.append("b", 123_i64); + doc.append("c", 0_i64); + }); +} + +#[test] +fn str() { + let expected = doc! { + "first": "the quick", + "second": "brown fox", + "third": "jumped over", + "last": "the lazy sheep dog", + }; + append_test(expected, |doc| { + doc.append("first", "the quick"); + doc.append("second", "brown fox"); + doc.append("third", "jumped over"); + doc.append("last", "the lazy sheep dog"); + }); +} + +#[test] +fn double() { + let expected = doc! { + "positive": 12.5, + "0": 0.0, + "negative": -123.24, + "nan": f64::NAN, + "inf": f64::INFINITY, + }; + append_test(expected, |doc| { + doc.append("positive", 12.5); + doc.append("0", 0.0); + doc.append("negative", -123.24); + doc.append("nan", f64::NAN); + doc.append("inf", f64::INFINITY); + }); +} + +#[test] +fn boolean() { + let expected = doc! { + "true": true, + "false": false, + }; + append_test(expected, |doc| { + doc.append("true", true); + doc.append("false", false); + }); +} + +#[test] +fn null() { + let expected = doc! { + "null": null, + }; + append_test(expected, |doc| { + doc.append("null", RawBson::Null); + }); +} + +#[test] +fn document() { + let expected = doc! { + "empty": {}, + "subdoc": { + "a": 1_i32, + "b": true, + } + }; + append_test(expected, |doc| { + doc.append("empty", RawDocumentBuf::new()); + let mut buf = RawDocumentBuf::new(); + buf.append("a", 1_i32); + buf.append("b", true); + doc.append("subdoc", buf); + }); +} + +#[test] +fn array() { + let expected = doc! { + "empty": [], + "array": [ + true, + "string", + { "a": "subdoc" }, + 123_i32 + ] + }; + append_test(expected, |doc| { + doc.append("empty", RawArrayBuf::new()); + let mut buf = RawArrayBuf::new(); + buf.push(true); + buf.push("string"); + let mut subdoc = RawDocumentBuf::new(); + subdoc.append("a", "subdoc"); + buf.push(subdoc); + buf.push(123_i32); + doc.append("array", buf); + }); +} + +#[test] +fn oid() { + let _guard = LOCK.run_concurrently(); + + let oid = ObjectId::new(); + let expected = doc! { + "oid": oid, + }; + append_test(expected, |doc| doc.append("oid", oid)); +} + +#[test] +fn datetime() { + let dt = DateTime::now(); + let old = DateTime::from_millis(-1); + + let expected = doc! { + "now": dt, + "old": old + }; + + append_test(expected, |doc| { + doc.append("now", dt); + doc.append("old", old); + }); +} + +#[test] +fn timestamp() { + let ts = Timestamp { + time: 123, + increment: 2, + }; + + let expected = doc! { + "ts": ts, + }; + + append_test(expected, |doc| { + doc.append("ts", ts); + }); +} + +#[test] +fn binary() { + let bytes = vec![1, 2, 3, 4]; + + let bin = Binary { + bytes: bytes.clone(), + subtype: BinarySubtype::Generic, + }; + + let old = Binary { + bytes, + subtype: BinarySubtype::BinaryOld, + }; + + let expected = doc! { + "generic": bin.clone(), + "binary_old": old.clone(), + }; + + append_test(expected, |doc| { + doc.append("generic", bin); + doc.append("binary_old", old); + }); +} + +#[test] +fn min_max_key() { + let expected = doc! { + "min": Bson::MinKey, + "max": Bson::MaxKey + }; + + append_test(expected, |doc| { + doc.append("min", RawBson::MinKey); + doc.append("max", RawBson::MaxKey); + }); +} + +#[test] +fn undefined() { + let expected = doc! { + "undefined": Bson::Undefined, + }; + + append_test(expected, |doc| { + doc.append("undefined", RawBson::Undefined); + }); +} + +#[test] +fn regex() { + let expected = doc! { + "regex": Regex::new("some pattern", "abc"), + }; + + append_test(expected, |doc| { + doc.append("regex", Regex::new("some pattern", "abc")); + }); +} + +#[test] +fn code() { + let code_w_scope = JavaScriptCodeWithScope { + code: "some code".to_string(), + scope: doc! { "a": 1_i32, "b": true }, + }; + + let expected = doc! { + "code": Bson::JavaScriptCode("some code".to_string()), + "code_w_scope": code_w_scope, + }; + + append_test(expected, |doc| { + doc.append("code", RawBson::JavaScriptCode("some code".to_string())); + + let mut scope = RawDocumentBuf::new(); + scope.append("a", 1_i32); + scope.append("b", true); + doc.append( + "code_w_scope", + RawJavaScriptCodeWithScope { + code: "some code".to_string(), + scope, + }, + ); + }); +} + +#[test] +fn symbol() { + let expected = doc! { + "symbol": Bson::Symbol("symbol".to_string()) + }; + + append_test(expected, |doc| { + doc.append("symbol", RawBson::Symbol("symbol".to_string())); + }); +} + +#[test] +fn dbpointer() { + let _guard = LOCK.run_concurrently(); + + let id = ObjectId::new(); + + let expected = doc! { + "symbol": Bson::DbPointer(DbPointer { + namespace: "ns".to_string(), + id + }) + }; + + append_test(expected, |doc| { + doc.append( + "symbol", + RawBson::DbPointer(DbPointer { + namespace: "ns".to_string(), + id, + }), + ); + }); +} + +#[test] +fn decimal128() { + let decimal = Decimal128 { bytes: [1; 16] }; + let expected = doc! { + "decimal": decimal + }; + + append_test(expected, |doc| { + doc.append("decimal", decimal); + }); +} + +#[test] +fn general() { + let dt = DateTime::now(); + let expected = doc! { + "a": true, + "second key": 123.4, + "third": 15_i64, + "32": -100101_i32, + "subdoc": { + "a": "subkey", + "another": { "subdoc": dt } + }, + "array": [1_i64, true, { "doc": 23_i64 }, ["another", "array"]], + }; + + append_test(expected, |doc| { + doc.append("a", true); + doc.append("second key", 123.4); + doc.append("third", 15_i64); + doc.append("32", -100101_i32); + + let mut subdoc = RawDocumentBuf::new(); + subdoc.append("a", "subkey"); + + let mut subsubdoc = RawDocumentBuf::new(); + subsubdoc.append("subdoc", dt); + subdoc.append("another", subsubdoc); + doc.append("subdoc", subdoc); + + let mut array = RawArrayBuf::new(); + array.push(1_i64); + array.push(true); + + let mut array_subdoc = RawDocumentBuf::new(); + array_subdoc.append("doc", 23_i64); + array.push(array_subdoc); + + let mut sub_array = RawArrayBuf::new(); + sub_array.push("another"); + sub_array.push("array"); + array.push(sub_array); + + doc.append("array", array); + }); +} + +#[test] +fn from_iter() { + let doc_buf = RawDocumentBuf::from_iter([ + ( + "array", + RawBson::Array(RawArrayBuf::from_iter([ + RawBson::Boolean(true), + RawBson::Document(RawDocumentBuf::from_iter([ + ("ok", RawBson::Boolean(false)), + ("other", RawBson::String("hello".to_string())), + ])), + ])), + ), + ("bool", RawBson::Boolean(true)), + ("string", RawBson::String("some string".to_string())), + ]); + + let doc = doc! { + "array": [ + true, + { + "ok": false, + "other": "hello" + } + ], + "bool": true, + "string": "some string" + }; + + let expected = doc! { "expected": doc }; + append_test(expected, |doc| { + doc.append("expected", doc_buf); + }); +} diff --git a/src/raw/test/mod.rs b/src/raw/test/mod.rs index e20a1bb4..d9460fde 100644 --- a/src/raw/test/mod.rs +++ b/src/raw/test/mod.rs @@ -1,3 +1,4 @@ +mod append; mod props; use super::*; @@ -28,7 +29,7 @@ fn string_from_document() { "that": "second", "something": "else", }); - let rawdoc = RawDocument::new(&docbytes).unwrap(); + let rawdoc = RawDocument::from_bytes(&docbytes).unwrap(); assert_eq!( rawdoc.get("that").unwrap().unwrap().as_str().unwrap(), "second", @@ -43,7 +44,7 @@ fn nested_document() { "i64": 6_i64, }, }); - let rawdoc = RawDocument::new(&docbytes).unwrap(); + let rawdoc = RawDocument::from_bytes(&docbytes).unwrap(); let subdoc = rawdoc .get("outer") .expect("get doc result") @@ -78,7 +79,7 @@ fn iterate() { "peanut butter": "chocolate", "easy as": {"do": 1, "re": 2, "mi": 3}, }); - let rawdoc = RawDocument::new(&docbytes).expect("malformed bson document"); + let rawdoc = RawDocument::from_bytes(&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"); @@ -115,7 +116,7 @@ fn rawdoc_to_doc() { "end": "END", }); - let rawdoc = RawDocument::new(&docbytes).expect("invalid document"); + let rawdoc = RawDocument::from_bytes(&docbytes).expect("invalid document"); let doc: crate::Document = rawdoc.try_into().expect("invalid bson"); let round_tripped_bytes = crate::to_vec(&doc).expect("serialize should work"); assert_eq!(round_tripped_bytes, docbytes); @@ -197,7 +198,7 @@ fn binary() { "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1u8, 2, 3] } }) .unwrap(); - let binary: bson::RawBinary<'_> = rawdoc + let binary: bson_ref::RawBinaryRef<'_> = rawdoc .get("binary") .expect("error finding key binary") .expect("no key binary") @@ -439,7 +440,7 @@ fn into_bson_conversion() { "binary": Binary { subtype: BinarySubtype::Generic, bytes: vec![1u8, 2, 3] }, "boolean": false, }); - let rawbson = RawBson::Document(RawDocument::new(docbytes.as_slice()).unwrap()); + let rawbson = RawBsonRef::Document(RawDocument::from_bytes(docbytes.as_slice()).unwrap()); 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)); @@ -486,14 +487,14 @@ use std::convert::TryInto; proptest! { #[test] fn no_crashes(s: Vec) { - let _ = RawDocumentBuf::new(s); + let _ = RawDocumentBuf::from_bytes(s); } #[test] fn roundtrip_bson(bson in arbitrary_bson()) { let doc = doc!{"bson": bson}; let raw = to_bytes(&doc); - let raw = RawDocumentBuf::new(raw); + let raw = RawDocumentBuf::from_bytes(raw); prop_assert!(raw.is_ok()); let raw = raw.unwrap(); let roundtrip: Result = raw.try_into(); diff --git a/src/ser/raw/value_serializer.rs b/src/ser/raw/value_serializer.rs index 49880a85..ad4d0812 100644 --- a/src/ser/raw/value_serializer.rs +++ b/src/ser/raw/value_serializer.rs @@ -10,6 +10,8 @@ use crate::{ raw::RAW_DOCUMENT_NEWTYPE, ser::{write_binary, write_cstring, write_i32, write_i64, write_string, Error, Result}, spec::{BinarySubtype, ElementType}, + RawDocument, + RawJavaScriptCodeWithScopeRef, }; use super::{document_serializer::DocumentSerializer, Serializer}; @@ -306,8 +308,11 @@ impl<'a, 'b> serde::Serializer for &'b mut ValueSerializer<'a> { Ok(()) } SerializationStep::CodeWithScopeScope { ref code, raw } if raw => { - let len = 4 + 4 + code.len() as i32 + 1 + v.len() as i32; - write_i32(&mut self.root_serializer.bytes, len)?; + let raw = RawJavaScriptCodeWithScopeRef { + code, + scope: RawDocument::from_bytes(v).map_err(Error::custom)?, + }; + write_i32(&mut self.root_serializer.bytes, raw.len())?; write_string(&mut self.root_serializer.bytes, code)?; self.root_serializer.bytes.write_all(v)?; self.state = SerializationStep::Done; diff --git a/src/ser/serde.rs b/src/ser/serde.rs index b8452e60..1b4b5b43 100644 --- a/src/ser/serde.rs +++ b/src/ser/serde.rs @@ -17,7 +17,7 @@ use crate::{ datetime::DateTime, extjson, oid::ObjectId, - raw::{RawDbPointer, RawRegex, RAW_ARRAY_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, + raw::{RawDbPointerRef, RawRegexRef, RAW_ARRAY_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, spec::BinarySubtype, uuid::UUID_NEWTYPE_NAME, Binary, @@ -648,7 +648,7 @@ impl Serialize for Regex { where S: ser::Serializer, { - let raw = RawRegex { + let raw = RawRegexRef { pattern: self.pattern.as_str(), options: self.options.as_str(), }; @@ -720,7 +720,7 @@ impl Serialize for DbPointer { where S: ser::Serializer, { - let raw = RawDbPointer { + let raw = RawDbPointerRef { namespace: self.namespace.as_str(), id: self.id, }; diff --git a/src/tests/spec/corpus.rs b/src/tests/spec/corpus.rs index b4329fb1..41159cd6 100644 --- a/src/tests/spec/corpus.rs +++ b/src/tests/spec/corpus.rs @@ -1,14 +1,17 @@ use std::{ convert::{TryFrom, TryInto}, + iter::FromIterator, marker::PhantomData, str::FromStr, }; use crate::{ - raw::{RawBson, RawDocument}, + raw::{RawBsonRef, RawDocument}, tests::LOCK, Bson, Document, + RawBson, + RawDocumentBuf, }; use pretty_assertions::assert_eq; use serde::{Deserialize, Deserializer}; @@ -114,14 +117,18 @@ fn run_test(test: TestFile) { crate::to_document(&documentfromreader_cb).expect(&description); let canonical_raw_document = - RawDocument::new(canonical_bson.as_slice()).expect(&description); + RawDocument::from_bytes(canonical_bson.as_slice()).expect(&description); let document_from_raw_document: Document = canonical_raw_document.try_into().expect(&description); - let canonical_raw_bson_from_slice = crate::from_slice::(canonical_bson.as_slice()) - .expect(&description) - .as_document() - .expect(&description); + let canonical_raw_bson_from_slice = + crate::from_slice::(canonical_bson.as_slice()) + .expect(&description) + .as_document() + .expect(&description); + + let canonical_owned_raw_bson_from_slice = + crate::from_slice::(canonical_bson.as_slice()).expect(&description); let canonical_raw_document_from_slice = crate::from_slice::<&RawDocument>(canonical_bson.as_slice()).expect(&description); @@ -160,6 +167,8 @@ fn run_test(test: TestFile) { let tovec_rawdocument_from_slice = crate::to_vec(&canonical_raw_document_from_slice).expect(&description); let tovec_rawbson = crate::to_vec(&canonical_raw_bson_from_slice).expect(&description); + let tovec_ownedrawbson = + crate::to_vec(&canonical_owned_raw_bson_from_slice).expect(&description); // test Bson / RawBson field deserialization if let Some(ref test_key) = test.test_key { @@ -169,7 +178,7 @@ fn run_test(test: TestFile) { let mut deserializer_raw = crate::de::RawDeserializer::new(canonical_bson.as_slice(), false); let raw_bson_field = deserializer_raw - .deserialize_any(FieldVisitor(test_key.as_str(), PhantomData::)) + .deserialize_any(FieldVisitor(test_key.as_str(), PhantomData::)) .expect(&description); // convert to an owned Bson and put into a Document let bson: Bson = raw_bson_field.try_into().expect(&description); @@ -177,6 +186,15 @@ fn run_test(test: TestFile) { test_key: bson }; + // deserialize the field from raw Bytes into an OwnedRawBson + let mut deserializer_raw = + crate::de::RawDeserializer::new(canonical_bson.as_slice(), false); + let owned_raw_bson_field = deserializer_raw + .deserialize_any(FieldVisitor(test_key.as_str(), PhantomData::)) + .expect(&description); + let from_slice_owned_vec = + RawDocumentBuf::from_iter([(test_key, owned_raw_bson_field)]).into_bytes(); + // deserialize the field from raw Bytes into a Bson let mut deserializer_value = crate::de::RawDeserializer::new(canonical_bson.as_slice(), false); @@ -184,7 +202,7 @@ fn run_test(test: TestFile) { .deserialize_any(FieldVisitor(test_key.as_str(), PhantomData::)) .expect(&description); // put into a Document - let from_value_doc = doc! { + let from_slice_value_doc = doc! { test_key: bson_field, }; @@ -201,13 +219,14 @@ fn run_test(test: TestFile) { // convert back into raw BSON for comparison with canonical BSON let from_raw_vec = crate::to_vec(&from_raw_doc).expect(&description); - let from_value_vec = crate::to_vec(&from_value_doc).expect(&description); - let from_value_value_vec = - crate::to_vec(&from_value_value_doc).expect(&description); + let from_slice_value_vec = + crate::to_vec(&from_slice_value_doc).expect(&description); + let from_bson_value_vec = crate::to_vec(&from_value_value_doc).expect(&description); assert_eq!(from_raw_vec, canonical_bson, "{}", description); - assert_eq!(from_value_vec, canonical_bson, "{}", description); - assert_eq!(from_value_value_vec, canonical_bson, "{}", description); + assert_eq!(from_slice_value_vec, canonical_bson, "{}", description); + assert_eq!(from_bson_value_vec, canonical_bson, "{}", description); + assert_eq!(from_slice_owned_vec, canonical_bson, "{}", description); } } @@ -263,6 +282,7 @@ fn run_test(test: TestFile) { "{}", description ); + assert_eq!(tovec_rawdocument, tovec_ownedrawbson, "{}", description); assert_eq!( hex::encode(tovec_rawdocument).to_lowercase(), @@ -324,7 +344,7 @@ fn run_test(test: TestFile) { description, ); - let document_from_raw_document: Document = RawDocument::new(db.as_slice()) + let document_from_raw_document: Document = RawDocument::from_bytes(db.as_slice()) .expect(&description) .try_into() .expect(&description); @@ -514,7 +534,7 @@ fn run_test(test: TestFile) { ); let bson = hex::decode(&decode_error.bson).expect("should decode from hex"); - if let Ok(doc) = RawDocument::new(bson.as_slice()) { + if let Ok(doc) = RawDocument::from_bytes(bson.as_slice()) { Document::try_from(doc).expect_err(description.as_str()); }