diff --git a/serde-tests/options.rs b/serde-tests/options.rs new file mode 100644 index 00000000..2032f800 --- /dev/null +++ b/serde-tests/options.rs @@ -0,0 +1,216 @@ +use std::collections::HashMap; + +use bson::{doc, Bson, DeserializerOptions, SerializerOptions}; + +use serde::{ + ser::{ + SerializeMap, + SerializeSeq, + SerializeStruct, + SerializeStructVariant, + SerializeTupleStruct, + SerializeTupleVariant, + }, + Deserialize, + Serialize, +}; + +/// Type whose serialize and deserialize implementations assert that the (de)serializer +/// is not human readable. +#[derive(Deserialize)] +struct Foo { + a: i32, + unit: Unit, + tuple: Tuple, + map: Map, + unit_variant: Bar, + tuple_variant: Bar, + struct_variant: Bar, + seq: Seq, +} + +impl Serialize for Foo { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + assert!(!serializer.is_human_readable()); + + let mut state = serializer.serialize_struct("Foo", 7)?; + state.serialize_field("a", &self.a)?; + state.serialize_field("unit", &self.unit)?; + state.serialize_field("tuple", &self.tuple)?; + state.serialize_field("map", &self.map)?; + state.serialize_field("unit_variant", &self.unit_variant)?; + state.serialize_field("tuple_variant", &self.tuple_variant)?; + state.serialize_field("struct_variant", &self.struct_variant)?; + state.serialize_field("seq", &self.seq)?; + state.end() + } +} + +#[derive(Deserialize)] +enum Bar { + Unit, + Tuple(Unit), + Struct { a: Unit }, +} + +impl Serialize for Bar { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + assert!(!serializer.is_human_readable()); + match self { + Self::Unit => serializer.serialize_unit_variant("Bar", 0, "Unit"), + Self::Tuple(t) => { + let mut state = serializer.serialize_tuple_variant("Bar", 1, "Tuple", 1)?; + state.serialize_field(t)?; + state.end() + } + Self::Struct { a } => { + let mut state = serializer.serialize_struct_variant("Foo", 2, "Struct", 1)?; + state.serialize_field("a", a)?; + state.end() + } + } + } +} + +struct Unit; + +impl Serialize for Unit { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + assert!(!serializer.is_human_readable()); + serializer.serialize_unit_struct("Unit") + } +} + +impl<'de> Deserialize<'de> for Unit { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + assert!(!deserializer.is_human_readable()); + Ok(Unit) + } +} + +#[derive(Deserialize)] +struct Tuple(Unit); + +impl Serialize for Tuple { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + assert!(!serializer.is_human_readable()); + let mut state = serializer.serialize_tuple_struct("Tuple", 1)?; + state.serialize_field(&self.0)?; + state.end() + } +} + +struct Map { + map: HashMap, +} + +impl Serialize for Map { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + assert!(!serializer.is_human_readable()); + + let mut state = serializer.serialize_map(Some(self.map.len()))?; + for (k, v) in self.map.iter() { + state.serialize_entry(k, &v)?; + } + state.end() + } +} + +impl<'de> Deserialize<'de> for Map { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + assert!(!deserializer.is_human_readable()); + let map = Deserialize::deserialize(deserializer)?; + Ok(Self { map }) + } +} + +struct Seq { + seq: Vec, +} + +impl Serialize for Seq { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + assert!(!serializer.is_human_readable()); + + let mut state = serializer.serialize_seq(Some(self.seq.len()))?; + for v in self.seq.iter() { + state.serialize_element(&v)?; + } + state.end() + } +} + +impl<'de> Deserialize<'de> for Seq { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + assert!(!deserializer.is_human_readable()); + let v = Vec::::deserialize(deserializer)?; + Ok(Self { seq: v }) + } +} + +#[test] +fn to_bson_with_options() { + let options = SerializerOptions::builder().human_readable(false).build(); + + let mut hm = HashMap::new(); + hm.insert("ok".to_string(), Unit); + hm.insert("other".to_string(), Unit); + let f = Foo { + a: 5, + unit: Unit, + tuple: Tuple(Unit), + unit_variant: Bar::Unit, + tuple_variant: Bar::Tuple(Unit), + struct_variant: Bar::Struct { a: Unit }, + map: Map { map: hm }, + seq: Seq { + seq: vec![Unit, Unit], + }, + }; + bson::to_bson_with_options(&f, options).unwrap(); +} + +#[test] +fn from_bson_with_options() { + let options = DeserializerOptions::builder().human_readable(false).build(); + + let doc = doc! { + "a": 5, + "unit": Bson::Null, + "tuple": [Bson::Null], + "unit_variant": { "Unit": Bson::Null }, + "tuple_variant": { "Tuple": [Bson::Null] }, + "struct_variant": { "Struct": { "a": Bson::Null } }, + "map": { "a": Bson::Null, "b": Bson::Null }, + "seq": [Bson::Null, Bson::Null], + }; + + let _: Foo = bson::from_bson_with_options(doc.into(), options).unwrap(); +} diff --git a/serde-tests/test.rs b/serde-tests/test.rs index 2db16982..875e4b5d 100644 --- a/serde-tests/test.rs +++ b/serde-tests/test.rs @@ -1,6 +1,8 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::vec_init_then_push)] +mod options; + use pretty_assertions::assert_eq; use serde::{ self, @@ -15,6 +17,7 @@ use std::{ }; use bson::{ + bson, doc, oid::ObjectId, spec::BinarySubtype, @@ -23,6 +26,7 @@ use bson::{ DateTime, Decimal128, Deserializer, + DeserializerOptions, Document, JavaScriptCodeWithScope, RawArray, @@ -34,6 +38,7 @@ use bson::{ RawJavaScriptCodeWithScope, RawRegex, Regex, + SerializerOptions, Timestamp, Uuid, }; @@ -75,6 +80,23 @@ where description ); + let non_human_readable_doc = bson::to_document_with_options( + &expected_value, + SerializerOptions::builder().human_readable(false).build(), + ) + .expect(description); + assert_eq!(&non_human_readable_doc, expected_doc, "{}", description); + assert_eq!( + expected_value, + &bson::from_document_with_options::( + non_human_readable_doc, + DeserializerOptions::builder().human_readable(false).build() + ) + .expect(description), + "{}", + description + ); + assert_eq!( &bson::from_reader::<_, T>(expected_bytes.as_slice()).expect(description), expected_value, @@ -1240,3 +1262,58 @@ fn hint_cleared() { assert_eq!(round_doc, doc! { "doc": doc_value, "binary": binary_value }); } + +#[test] +fn non_human_readable() { + let bytes = vec![1, 2, 3, 4]; + let binary = RawBinary { + 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 arr = doc.get_array("array").unwrap(); + let oid = ObjectId::new(); + let uuid = Uuid::new(); + + #[derive(Debug, Deserialize, Serialize)] + struct Foo<'a> { + #[serde(borrow)] + binary: RawBinary<'a>, + #[serde(borrow)] + doc: &'a RawDocument, + #[serde(borrow)] + arr: &'a RawArray, + oid: ObjectId, + uuid: Uuid, + } + + let val = Foo { + binary, + doc, + arr, + oid, + uuid, + }; + + let human_readable = bson::to_bson(&val).unwrap(); + let non_human_readable = bson::to_bson_with_options( + &val, + SerializerOptions::builder().human_readable(false).build(), + ) + .unwrap(); + + let expected = bson!({ + "binary": Binary { bytes: bytes.clone(), subtype: BinarySubtype::BinaryOld }, + "doc": { + "a": "b", + "array": [1, 2, 3], + }, + "arr": [1, 2, 3], + "oid": oid, + "uuid": uuid + }); + assert_eq!(human_readable, expected); + assert_eq!(human_readable, non_human_readable); +} diff --git a/src/bson.rs b/src/bson.rs index f4432ade..08167f35 100644 --- a/src/bson.rs +++ b/src/bson.rs @@ -1062,19 +1062,30 @@ impl Display for Binary { impl Binary { pub(crate) fn from_extended_doc(doc: &Document) -> Option { - let binary = doc.get_document("$binary").ok()?; - let bytes = binary.get_str("base64").ok()?; - let bytes = base64::decode(bytes).ok()?; - let subtype = binary.get_str("subType").ok()?; - let subtype = hex::decode(subtype).ok()?; + let binary_doc = doc.get_document("$binary").ok()?; + + if let Ok(bytes) = binary_doc.get_str("base64") { + let bytes = base64::decode(bytes).ok()?; + let subtype = binary_doc.get_str("subType").ok()?; + let subtype = hex::decode(subtype).ok()?; + if subtype.len() == 1 { + Some(Self { + bytes, + subtype: subtype[0].into(), + }) + } else { + None + } + } else { + // in non-human-readable mode, RawBinary will serialize as + // { "$binary": { "bytes": , "subType": } }; + let binary = binary_doc.get_binary_generic("bytes").ok()?; + let subtype = binary_doc.get_i32("subType").ok()?; - if subtype.len() == 1 { Some(Self { - bytes, - subtype: subtype[0].into(), + bytes: binary.clone(), + subtype: u8::try_from(subtype).ok()?.into(), }) - } else { - None } } } diff --git a/src/de/mod.rs b/src/de/mod.rs index c577c891..3a8fef0d 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -27,7 +27,7 @@ mod serde; pub use self::{ error::{Error, Result}, - serde::Deserializer, + serde::{Deserializer, DeserializerOptions}, }; use std::io::Read; @@ -414,7 +414,14 @@ impl JavaScriptCodeWithScope { } } -/// Decode a BSON `Value` into a `T` Deserializable. +/// Deserialize a `T` from the provided [`Bson`] value. +/// +/// The `Deserializer` used by this function presents itself as human readable, whereas the +/// one used in [`from_slice`] does not. This means that this function may deserialize differently +/// than [`from_slice`] for types that change their deserialization logic depending on whether +/// the format is human readable or not. To deserialize from [`Bson`] with a deserializer that +/// presents itself as not human readable, use [`from_bson_with_options`] with +/// [`DeserializerOptions::human_readable`] set to false. pub fn from_bson(bson: Bson) -> Result where T: DeserializeOwned, @@ -423,7 +430,38 @@ where Deserialize::deserialize(de) } -/// Decode a BSON `Document` into a `T` Deserializable. +/// Deserialize a `T` from the provided [`Bson`] value, configuring the underlying +/// deserializer with the provided options. +/// ``` +/// # use serde::Deserialize; +/// # use bson::{bson, DeserializerOptions}; +/// #[derive(Debug, Deserialize, PartialEq)] +/// struct MyData { +/// a: String, +/// } +/// +/// let bson = bson!({ "a": "hello" }); +/// let options = DeserializerOptions::builder().human_readable(false).build(); +/// let data: MyData = bson::from_bson_with_options(bson, options)?; +/// assert_eq!(data, MyData { a: "hello".to_string() }); +/// # Ok::<(), Box>(()) +/// ``` +pub fn from_bson_with_options(bson: Bson, options: DeserializerOptions) -> Result +where + T: DeserializeOwned, +{ + let de = Deserializer::new_with_options(bson, options); + Deserialize::deserialize(de) +} + +/// Deserialize a `T` from the provided [`Document`]. +/// +/// The `Deserializer` used by this function presents itself as human readable, whereas the +/// one used in [`from_slice`] does not. This means that this function may deserialize differently +/// than [`from_slice`] for types that change their deserialization logic depending on whether +/// the format is human readable or not. To deserialize from [`Document`] with a deserializer that +/// presents itself as not human readable, use [`from_document_with_options`] with +/// [`DeserializerOptions::human_readable`] set to false. pub fn from_document(doc: Document) -> Result where T: DeserializeOwned, @@ -431,6 +469,30 @@ where from_bson(Bson::Document(doc)) } +/// Deserialize a `T` from the provided [`Document`], configuring the underlying +/// deserializer with the provided options. +/// ``` +/// # use serde::Deserialize; +/// # use bson::{doc, DeserializerOptions}; +/// #[derive(Debug, Deserialize, PartialEq)] +/// struct MyData { +/// a: String, +/// } +/// +/// let doc = doc! { "a": "hello" }; +/// let options = DeserializerOptions::builder().human_readable(false).build(); +/// let data: MyData = bson::from_document_with_options(doc, options)?; +/// assert_eq!(data, MyData { a: "hello".to_string() }); +/// # Ok::<(), Box>(()) +/// ``` +pub fn from_document_with_options(doc: Document, options: DeserializerOptions) -> Result +where + T: DeserializeOwned, +{ + let de = Deserializer::new_with_options(Bson::Document(doc), options); + Deserialize::deserialize(de) +} + fn reader_to_vec(mut reader: R) -> Result> { let length = read_i32(&mut reader)?; diff --git a/src/de/raw.rs b/src/de/raw.rs index 48a20de4..05817831 100644 --- a/src/de/raw.rs +++ b/src/de/raw.rs @@ -1179,7 +1179,7 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut BinaryDeserializer<'de> { BinaryDeserializationStage::Subtype => { self.stage = BinaryDeserializationStage::Bytes; match self.hint { - DeserializerHint::RawBson => visitor.visit_u8(self.binary.subtype().into()), + DeserializerHint::RawBson => visitor.visit_u8(self.binary.subtype.into()), _ => visitor.visit_string(hex::encode([u8::from(self.binary.subtype)])), } } diff --git a/src/de/serde.rs b/src/de/serde.rs index 23fc5e8d..dcc0bd65 100644 --- a/src/de/serde.rs +++ b/src/de/serde.rs @@ -562,11 +562,58 @@ where /// Serde Deserializer pub struct Deserializer { value: Option, + options: DeserializerOptions, +} + +/// Options used to configure a [`Deserializer`]. These can also be passed into +/// [`crate::from_bson_with_options`] and [`crate::from_document_with_options`]. +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct DeserializerOptions { + /// Whether the [`Deserializer`] should present itself as human readable or not. + /// The default is true. + pub human_readable: Option, +} + +impl DeserializerOptions { + /// Create a builder struct used to construct a [`DeserializerOptions`]. + pub fn builder() -> DeserializerOptionsBuilder { + DeserializerOptionsBuilder { + options: Default::default(), + } + } +} + +/// Builder used to construct a [`DeserializerOptions`]. +pub struct DeserializerOptionsBuilder { + options: DeserializerOptions, +} + +impl DeserializerOptionsBuilder { + /// Set the value for [`DeserializerOptions::human_readable`]. + pub fn human_readable(mut self, val: impl Into>) -> Self { + self.options.human_readable = val.into(); + self + } + + /// Consume this builder and produce a [`DeserializerOptions`]. + pub fn build(self) -> DeserializerOptions { + self.options + } } impl Deserializer { + /// Construct a new [`Deserializer`] using the default options. pub fn new(value: Bson) -> Deserializer { - Deserializer { value: Some(value) } + Deserializer::new_with_options(value, Default::default()) + } + + /// Create a new [`Deserializer`] using the provided options. + pub fn new_with_options(value: Bson, options: DeserializerOptions) -> Self { + Deserializer { + value: Some(value), + options, + } } } @@ -610,6 +657,10 @@ macro_rules! forward_to_deserialize { impl<'de> de::Deserializer<'de> for Deserializer { type Error = crate::de::Error; + fn is_human_readable(&self) -> bool { + self.options.human_readable.unwrap_or(true) + } + #[inline] fn deserialize_any(mut self, visitor: V) -> crate::de::Result where @@ -627,6 +678,7 @@ impl<'de> de::Deserializer<'de> for Deserializer { let len = v.len(); visitor.visit_seq(SeqDeserializer { iter: v.into_iter(), + options: self.options, len, }) } @@ -636,6 +688,7 @@ impl<'de> de::Deserializer<'de> for Deserializer { iter: v.into_iter(), value: None, len, + options: self.options, }) } Bson::Boolean(v) => visitor.visit_bool(v), @@ -650,6 +703,7 @@ impl<'de> de::Deserializer<'de> for Deserializer { iter: binary.into_extended_document().into_iter(), value: None, len: 2, + options: self.options, }), Bson::Decimal128(d) => visitor.visit_map(Decimal128Access::new(d)), _ => { @@ -659,11 +713,25 @@ impl<'de> de::Deserializer<'de> for Deserializer { iter: doc.into_iter(), value: None, len, + options: self.options, }) } } } + #[inline] + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.value { + Some(Bson::ObjectId(oid)) if !self.is_human_readable() => { + visitor.visit_bytes(&oid.bytes()) + } + _ => self.deserialize_any(visitor), + } + } + #[inline] fn deserialize_option(self, visitor: V) -> crate::de::Result where @@ -691,7 +759,10 @@ impl<'de> de::Deserializer<'de> for Deserializer { Some(Bson::String(variant)) => { return visitor.visit_enum(EnumDeserializer { val: Bson::String(variant), - deserializer: VariantDeserializer { val: None }, + deserializer: VariantDeserializer { + val: None, + options: self.options, + }, }); } Some(v) => { @@ -725,7 +796,10 @@ impl<'de> de::Deserializer<'de> for Deserializer { )), None => visitor.visit_enum(EnumDeserializer { val: Bson::String(variant), - deserializer: VariantDeserializer { val: Some(value) }, + deserializer: VariantDeserializer { + val: Some(value), + options: self.options, + }, }), } } @@ -772,7 +846,6 @@ impl<'de> de::Deserializer<'de> for Deserializer { deserialize_string(); deserialize_unit(); deserialize_seq(); - deserialize_bytes(); deserialize_map(); deserialize_unit_struct(name: &'static str); deserialize_tuple_struct(name: &'static str, len: usize); @@ -796,7 +869,7 @@ impl<'de> EnumAccess<'de> for EnumDeserializer { where V: DeserializeSeed<'de>, { - let dec = Deserializer::new(self.val); + let dec = Deserializer::new_with_options(self.val, self.deserializer.options.clone()); let value = seed.deserialize(dec)?; Ok((value, self.deserializer)) } @@ -804,6 +877,7 @@ impl<'de> EnumAccess<'de> for EnumDeserializer { struct VariantDeserializer { val: Option, + options: DeserializerOptions, } impl<'de> VariantAccess<'de> for VariantDeserializer { @@ -812,7 +886,9 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { fn unit_variant(mut self) -> crate::de::Result<()> { match self.val.take() { None => Ok(()), - Some(val) => Bson::deserialize(Deserializer::new(val)).map(|_| ()), + Some(val) => { + Bson::deserialize(Deserializer::new_with_options(val, self.options)).map(|_| ()) + } } } @@ -820,7 +896,10 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { where T: DeserializeSeed<'de>, { - let dec = Deserializer::new(self.val.take().ok_or(crate::de::Error::EndOfStream)?); + let dec = Deserializer::new_with_options( + self.val.take().ok_or(crate::de::Error::EndOfStream)?, + self.options, + ); seed.deserialize(dec) } @@ -833,6 +912,7 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { let de = SeqDeserializer { len: fields.len(), iter: fields.into_iter(), + options: self.options, }; de.deserialize_any(visitor) } @@ -857,6 +937,7 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { len: fields.len(), iter: fields.into_iter(), value: None, + options: self.options, }; de.deserialize_any(visitor) } @@ -871,6 +952,7 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { struct SeqDeserializer { iter: vec::IntoIter, len: usize, + options: DeserializerOptions, } impl<'de> de::Deserializer<'de> for SeqDeserializer { @@ -931,7 +1013,7 @@ impl<'de> SeqAccess<'de> for SeqDeserializer { None => Ok(None), Some(value) => { self.len -= 1; - let de = Deserializer::new(value); + let de = Deserializer::new_with_options(value, self.options.clone()); match seed.deserialize(de) { Ok(value) => Ok(Some(value)), Err(err) => Err(err), @@ -949,6 +1031,7 @@ pub(crate) struct MapDeserializer { pub(crate) iter: IntoIter, pub(crate) value: Option, pub(crate) len: usize, + pub(crate) options: DeserializerOptions, } impl MapDeserializer { @@ -958,6 +1041,7 @@ impl MapDeserializer { iter: doc.into_iter(), len, value: None, + options: Default::default(), } } } @@ -974,7 +1058,7 @@ impl<'de> MapAccess<'de> for MapDeserializer { self.len -= 1; self.value = Some(value); - let de = Deserializer::new(Bson::String(key)); + let de = Deserializer::new_with_options(Bson::String(key), self.options.clone()); match seed.deserialize(de) { Ok(val) => Ok(Some(val)), Err(e) => Err(e), @@ -989,7 +1073,7 @@ impl<'de> MapAccess<'de> for MapDeserializer { V: DeserializeSeed<'de>, { let value = self.value.take().ok_or(crate::de::Error::EndOfStream)?; - let de = Deserializer::new(value); + let de = Deserializer::new_with_options(value, self.options.clone()); seed.deserialize(de) } diff --git a/src/lib.rs b/src/lib.rs index ff7ae64a..cd03c9e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -271,15 +271,18 @@ pub use self::{ bson::{Array, Binary, Bson, DbPointer, Document, JavaScriptCodeWithScope, Regex, Timestamp}, datetime::DateTime, de::{ - from_bson, from_document, from_reader, from_reader_utf8_lossy, from_slice, - from_slice_utf8_lossy, Deserializer, + 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, }, - ser::{to_bson, to_document, to_vec, Serializer}, + ser::{ + to_bson, to_bson_with_options, to_document, to_document_with_options, to_vec, Serializer, + SerializerOptions, + }, uuid::{Uuid, UuidRepresentation}, }; diff --git a/src/raw/bson.rs b/src/raw/bson.rs index 9529a1e7..3402c11b 100644 --- a/src/raw/bson.rs +++ b/src/raw/bson.rs @@ -610,20 +610,11 @@ impl<'a> TryFrom> for Bson { /// A BSON binary value referencing raw bytes stored elsewhere. #[derive(Clone, Copy, Debug, PartialEq)] pub struct RawBinary<'a> { - pub(crate) subtype: BinarySubtype, - pub(crate) bytes: &'a [u8], -} - -impl<'a> RawBinary<'a> { - /// Gets the subtype of the binary value. - pub fn subtype(self) -> BinarySubtype { - self.subtype - } + /// The subtype of the binary value. + pub subtype: BinarySubtype, - /// Gets the contained bytes of the binary value. - pub fn as_bytes(self) -> &'a [u8] { - self.bytes - } + /// The binary bytes. + pub bytes: &'a [u8], } impl<'de: 'a, 'a> Deserialize<'de> for RawBinary<'a> { diff --git a/src/raw/document.rs b/src/raw/document.rs index 053b64d0..34c1ef45 100644 --- a/src/raw/document.rs +++ b/src/raw/document.rs @@ -312,7 +312,7 @@ impl RawDocument { /// "bool": true, /// })?; /// - /// assert_eq!(doc.get_binary("binary")?.as_bytes(), &[1, 2, 3][..]); + /// assert_eq!(&doc.get_binary("binary")?.bytes, &[1, 2, 3]); /// assert!(matches!(doc.get_binary("bool").unwrap_err().kind, ValueAccessErrorKind::UnexpectedType { .. })); /// assert!(matches!(doc.get_binary("unknown").unwrap_err().kind, ValueAccessErrorKind::NotPresent)); /// # Ok::<(), Box>(()) diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 0a2f65a0..e133d952 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -27,7 +27,7 @@ mod serde; pub use self::{ error::{Error, Result}, - serde::Serializer, + serde::{Serializer, SerializerOptions}, }; use std::{io::Write, iter::FromIterator, mem}; @@ -185,6 +185,13 @@ pub(crate) fn serialize_bson( } /// Encode a `T` Serializable into a BSON `Value`. +/// +/// The `Serializer` used by this function presents itself as human readable, whereas the +/// one used in [`to_vec`] does not. This means that this function will produce different BSON than +/// [`to_vec`] for types that change their serialization output depending on whether +/// the format is human readable or not. To serialize to a [`Document`] with a serializer that +/// presents itself as not human readable, use [`to_bson_with_options`] with +/// [`SerializerOptions::human_readable`] set to false. pub fn to_bson(value: &T) -> Result where T: Serialize, @@ -193,12 +200,69 @@ where value.serialize(ser) } +/// Encode a `T` into a `Bson` value, configuring the underlying serializer with the provided +/// options. +/// ``` +/// # use serde::Serialize; +/// # use bson::{bson, SerializerOptions}; +/// #[derive(Debug, Serialize)] +/// struct MyData { +/// a: String, +/// } +/// +/// let data = MyData { a: "ok".to_string() }; +/// let options = SerializerOptions::builder().human_readable(false).build(); +/// let bson = bson::to_bson_with_options(&data, options)?; +/// assert_eq!(bson, bson!({ "a": "ok" })); +/// # Ok::<(), Box>(()) +/// ``` +pub fn to_bson_with_options(value: &T, options: SerializerOptions) -> Result +where + T: Serialize, +{ + let ser = Serializer::new_with_options(options); + value.serialize(ser) +} + /// Encode a `T` Serializable into a BSON `Document`. +/// +/// The `Serializer` used by this function presents itself as human readable, whereas the +/// one used in [`to_vec`] does not. This means that this function will produce different BSON than +/// [`to_vec`] for types that change their serialization output depending on whether +/// the format is human readable or not. To serialize to a [`Document`] with a serializer that +/// presents itself as not human readable, use [`to_document_with_options`] with +/// [`SerializerOptions::human_readable`] set to false. pub fn to_document(value: &T) -> Result where T: Serialize, { - match to_bson(value)? { + to_document_with_options(value, Default::default()) +} + +/// Encode a `T` into a [`Document`], configuring the underlying serializer with the provided +/// options. +/// ``` +/// # use serde::Serialize; +/// # use bson::{doc, SerializerOptions}; +/// #[derive(Debug, Serialize)] +/// struct MyData { +/// a: String, +/// } +/// +/// let data = MyData { a: "ok".to_string() }; +/// let options = SerializerOptions::builder().human_readable(false).build(); +/// let doc = bson::to_document_with_options(&data, options)?; +/// assert_eq!(doc, doc! { "a": "ok" }); +/// # Ok::<(), Box>(()) +/// ``` +pub fn to_document_with_options( + value: &T, + options: SerializerOptions, +) -> Result +where + T: Serialize, +{ + match to_bson_with_options(value, options)? { Bson::Document(doc) => Ok(doc), bson => Err(Error::SerializationError { message: format!( diff --git a/src/ser/serde.rs b/src/ser/serde.rs index 046978d8..b8452e60 100644 --- a/src/ser/serde.rs +++ b/src/ser/serde.rs @@ -17,14 +17,14 @@ use crate::{ datetime::DateTime, extjson, oid::ObjectId, - raw::{RawDbPointer, RawRegex}, + raw::{RawDbPointer, RawRegex, RAW_ARRAY_NEWTYPE, RAW_DOCUMENT_NEWTYPE}, spec::BinarySubtype, uuid::UUID_NEWTYPE_NAME, Binary, Decimal128, }; -use super::{to_bson, Error}; +use super::{to_bson_with_options, Error}; impl Serialize for ObjectId { #[inline] @@ -110,13 +110,58 @@ impl Serialize for Bson { /// Serde Serializer #[non_exhaustive] -pub struct Serializer; +pub struct Serializer { + options: SerializerOptions, +} + +/// Options used to configure a [`Serializer`]. +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct SerializerOptions { + /// Whether the [`Serializer`] should present itself as human readable or not. + /// The default value is true. + pub human_readable: Option, +} + +impl SerializerOptions { + /// Create a builder used to construct a new [`SerializerOptions`]. + pub fn builder() -> SerializerOptionsBuilder { + SerializerOptionsBuilder { + options: Default::default(), + } + } +} + +/// A builder used to construct new [`SerializerOptions`] structs. +pub struct SerializerOptionsBuilder { + options: SerializerOptions, +} + +impl SerializerOptionsBuilder { + /// Set the value for [`SerializerOptions::is_human_readable`]. + pub fn human_readable(mut self, value: impl Into>) -> Self { + self.options.human_readable = value.into(); + self + } + + /// Consume this builder and produce a [`SerializerOptions`]. + pub fn build(self) -> SerializerOptions { + self.options + } +} impl Serializer { /// Construct a new `Serializer`. #[allow(clippy::new_without_default)] pub fn new() -> Serializer { - Serializer + Serializer { + options: Default::default(), + } + } + + /// Construct a new `Serializer` configured with the provided [`SerializerOptions`]. + pub fn new_with_options(options: SerializerOptions) -> Self { + Serializer { options } } } @@ -258,21 +303,52 @@ impl ser::Serializer for Serializer { where T: Serialize, { - if name == UUID_NEWTYPE_NAME { - match value.serialize(self)? { - Bson::String(s) => { - // the serializer reports itself as human readable, so `Uuid` will - // serialize itself as a string. - let uuid = crate::Uuid::parse_str(s).map_err(Error::custom)?; - Ok(Bson::Binary(uuid.into())) + match name { + UUID_NEWTYPE_NAME => { + let is_human_readable = self.is_human_readable(); + match value.serialize(self)? { + Bson::String(s) if is_human_readable => { + // the serializer reports itself as human readable, so `Uuid` will + // serialize itself as a string. + let uuid = crate::Uuid::parse_str(s).map_err(Error::custom)?; + Ok(Bson::Binary(uuid.into())) + } + Bson::Binary(b) if !is_human_readable => Ok(Bson::Binary(Binary { + bytes: b.bytes, + subtype: BinarySubtype::Uuid, + })), + b => { + let expectation = if is_human_readable { + "a string" + } else { + "bytes" + }; + Err(Error::custom(format!( + "expected UUID to be serialized as {} but got {:?} instead", + expectation, b + ))) + } + } + } + // when in non-human-readable mode, raw document / raw array will serialize as bytes. + RAW_DOCUMENT_NEWTYPE | RAW_ARRAY_NEWTYPE if !self.is_human_readable() => match value + .serialize(self)? + { + Bson::Binary(b) => { + let doc = Document::from_reader(b.bytes.as_slice()).map_err(Error::custom)?; + + if name == RAW_DOCUMENT_NEWTYPE { + Ok(Bson::Document(doc)) + } else { + Ok(Bson::Array(doc.into_iter().map(|kvp| kvp.1).collect())) + } } b => Err(Error::custom(format!( - "expected UUID to be serialized as a string but got {:?} instead", + "expected raw document or array to be serialized as bytes but got {:?} instead", b ))), - } - } else { - value.serialize(self) + }, + _ => value.serialize(self), } } @@ -288,7 +364,7 @@ impl ser::Serializer for Serializer { T: Serialize, { let mut newtype_variant = Document::new(); - newtype_variant.insert(variant, to_bson(value)?); + newtype_variant.insert(variant, to_bson_with_options(value, self.options)?); Ok(newtype_variant.into()) } @@ -296,6 +372,7 @@ impl ser::Serializer for Serializer { fn serialize_seq(self, len: Option) -> crate::ser::Result { Ok(ArraySerializer { inner: Array::with_capacity(len.unwrap_or(0)), + options: self.options, }) } @@ -303,6 +380,7 @@ impl ser::Serializer for Serializer { fn serialize_tuple(self, len: usize) -> crate::ser::Result { Ok(TupleSerializer { inner: Array::with_capacity(len), + options: self.options, }) } @@ -314,6 +392,7 @@ impl ser::Serializer for Serializer { ) -> crate::ser::Result { Ok(TupleStructSerializer { inner: Array::with_capacity(len), + options: self.options, }) } @@ -328,6 +407,7 @@ impl ser::Serializer for Serializer { Ok(TupleVariantSerializer { inner: Array::with_capacity(len), name: variant, + options: self.options, }) } @@ -336,6 +416,7 @@ impl ser::Serializer for Serializer { Ok(MapSerializer { inner: Document::new(), next_key: None, + options: self.options, }) } @@ -347,6 +428,7 @@ impl ser::Serializer for Serializer { ) -> crate::ser::Result { Ok(StructSerializer { inner: Document::new(), + options: self.options, }) } @@ -361,13 +443,19 @@ impl ser::Serializer for Serializer { Ok(StructVariantSerializer { name: variant, inner: Document::new(), + options: self.options, }) } + + fn is_human_readable(&self) -> bool { + self.options.human_readable.unwrap_or(true) + } } #[doc(hidden)] pub struct ArraySerializer { inner: Array, + options: SerializerOptions, } impl SerializeSeq for ArraySerializer { @@ -375,7 +463,8 @@ impl SerializeSeq for ArraySerializer { type Error = Error; fn serialize_element(&mut self, value: &T) -> crate::ser::Result<()> { - self.inner.push(to_bson(value)?); + self.inner + .push(to_bson_with_options(value, self.options.clone())?); Ok(()) } @@ -387,6 +476,7 @@ impl SerializeSeq for ArraySerializer { #[doc(hidden)] pub struct TupleSerializer { inner: Array, + options: SerializerOptions, } impl SerializeTuple for TupleSerializer { @@ -394,7 +484,8 @@ impl SerializeTuple for TupleSerializer { type Error = Error; fn serialize_element(&mut self, value: &T) -> crate::ser::Result<()> { - self.inner.push(to_bson(value)?); + self.inner + .push(to_bson_with_options(value, self.options.clone())?); Ok(()) } @@ -406,6 +497,7 @@ impl SerializeTuple for TupleSerializer { #[doc(hidden)] pub struct TupleStructSerializer { inner: Array, + options: SerializerOptions, } impl SerializeTupleStruct for TupleStructSerializer { @@ -413,7 +505,8 @@ impl SerializeTupleStruct for TupleStructSerializer { type Error = Error; fn serialize_field(&mut self, value: &T) -> crate::ser::Result<()> { - self.inner.push(to_bson(value)?); + self.inner + .push(to_bson_with_options(value, self.options.clone())?); Ok(()) } @@ -426,6 +519,7 @@ impl SerializeTupleStruct for TupleStructSerializer { pub struct TupleVariantSerializer { inner: Array, name: &'static str, + options: SerializerOptions, } impl SerializeTupleVariant for TupleVariantSerializer { @@ -433,7 +527,8 @@ impl SerializeTupleVariant for TupleVariantSerializer { type Error = Error; fn serialize_field(&mut self, value: &T) -> crate::ser::Result<()> { - self.inner.push(to_bson(value)?); + self.inner + .push(to_bson_with_options(value, self.options.clone())?); Ok(()) } @@ -448,6 +543,7 @@ impl SerializeTupleVariant for TupleVariantSerializer { pub struct MapSerializer { inner: Document, next_key: Option, + options: SerializerOptions, } impl SerializeMap for MapSerializer { @@ -455,7 +551,7 @@ impl SerializeMap for MapSerializer { type Error = Error; fn serialize_key(&mut self, key: &T) -> crate::ser::Result<()> { - self.next_key = match to_bson(&key)? { + self.next_key = match to_bson_with_options(&key, self.options.clone())? { Bson::String(s) => Some(s), other => return Err(Error::InvalidDocumentKey(other)), }; @@ -464,7 +560,8 @@ impl SerializeMap for MapSerializer { fn serialize_value(&mut self, value: &T) -> crate::ser::Result<()> { let key = self.next_key.take().unwrap_or_default(); - self.inner.insert(key, to_bson(&value)?); + self.inner + .insert(key, to_bson_with_options(&value, self.options.clone())?); Ok(()) } @@ -476,6 +573,7 @@ impl SerializeMap for MapSerializer { #[doc(hidden)] pub struct StructSerializer { inner: Document, + options: SerializerOptions, } impl SerializeStruct for StructSerializer { @@ -487,7 +585,8 @@ impl SerializeStruct for StructSerializer { key: &'static str, value: &T, ) -> crate::ser::Result<()> { - self.inner.insert(key, to_bson(value)?); + self.inner + .insert(key, to_bson_with_options(value, self.options.clone())?); Ok(()) } @@ -500,6 +599,7 @@ impl SerializeStruct for StructSerializer { pub struct StructVariantSerializer { inner: Document, name: &'static str, + options: SerializerOptions, } impl SerializeStructVariant for StructVariantSerializer { @@ -511,7 +611,8 @@ impl SerializeStructVariant for StructVariantSerializer { key: &'static str, value: &T, ) -> crate::ser::Result<()> { - self.inner.insert(key, to_bson(value)?); + self.inner + .insert(key, to_bson_with_options(value, self.options.clone())?); Ok(()) }