Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions serde-tests/options.rs
Original file line number Diff line number Diff line change
@@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
assert!(!serializer.is_human_readable());
serializer.serialize_unit_struct("Unit")
}
}

impl<'de> Deserialize<'de> for Unit {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
assert!(!deserializer.is_human_readable());
Ok(Unit)
}
}

#[derive(Deserialize)]
struct Tuple(Unit);

impl Serialize for Tuple {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<String, Unit>,
}

impl Serialize for Map {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
assert!(!deserializer.is_human_readable());
let map = Deserialize::deserialize(deserializer)?;
Ok(Self { map })
}
}

struct Seq {
seq: Vec<Unit>,
}

impl Serialize for Seq {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
assert!(!deserializer.is_human_readable());
let v = Vec::<Unit>::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();
}
77 changes: 77 additions & 0 deletions serde-tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::vec_init_then_push)]

mod options;

use pretty_assertions::assert_eq;
use serde::{
self,
Expand All @@ -15,6 +17,7 @@ use std::{
};

use bson::{
bson,
doc,
oid::ObjectId,
spec::BinarySubtype,
Expand All @@ -23,6 +26,7 @@ use bson::{
DateTime,
Decimal128,
Deserializer,
DeserializerOptions,
Document,
JavaScriptCodeWithScope,
RawArray,
Expand All @@ -34,6 +38,7 @@ use bson::{
RawJavaScriptCodeWithScope,
RawRegex,
Regex,
SerializerOptions,
Timestamp,
Uuid,
};
Expand Down Expand Up @@ -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::<T>(
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,
Expand Down Expand Up @@ -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);
}
31 changes: 21 additions & 10 deletions src/bson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,19 +1062,30 @@ impl Display for Binary {

impl Binary {
pub(crate) fn from_extended_doc(doc: &Document) -> Option<Self> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RawBinary uses is_human_readable to determine if it should serialize to extjson or a more compact form. Since the Bson based serializer/deserializer can now present itself as not-human-readable, we need to add some special handling to them for when BSON types use a different serialization output.

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": <bytes>, "subType": <i32> } };
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
}
}
}
Expand Down
Loading