Skip to content

Commit

Permalink
RUST-1764 Add HumanReadable convenience wrapper (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
abr-egn authored Apr 5, 2024
1 parent c5be950 commit 9c4b963
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 7 deletions.
1 change: 0 additions & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ combine_control_expr = false
comment_width = 100
condense_wildcard_suffixes = true
format_strings = true
normalize_comments = true
use_try_shorthand = true
wrap_comments = true
imports_layout = "HorizontalVertical"
Expand Down
15 changes: 14 additions & 1 deletion src/de/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use serde::{
use crate::{
oid::ObjectId,
raw::{RawBinaryRef, RAW_ARRAY_NEWTYPE, RAW_BSON_NEWTYPE, RAW_DOCUMENT_NEWTYPE},
serde_helpers::HUMAN_READABLE_NEWTYPE,
spec::{BinarySubtype, ElementType},
uuid::UUID_NEWTYPE_NAME,
Bson,
Expand Down Expand Up @@ -51,6 +52,8 @@ pub(crate) struct Deserializer<'de> {
/// but given that there's no difference between deserializing an embedded document and a
/// top level one, the distinction isn't necessary.
current_type: ElementType,

human_readable: bool,
}

/// Enum used to determine what the type of document being deserialized is in
Expand All @@ -65,6 +68,7 @@ impl<'de> Deserializer<'de> {
Self {
bytes: BsonBuf::new(buf, utf8_lossy),
current_type: ElementType::EmbeddedDocument,
human_readable: false,
}
}

Expand Down Expand Up @@ -291,6 +295,7 @@ impl<'de> Deserializer<'de> {
let doc = Bson::JavaScriptCode(code).into_extended_document(false);
visitor.visit_map(MapDeserializer::new(
doc,
#[allow(deprecated)]
DeserializerOptions::builder().human_readable(false).build(),
))
}
Expand Down Expand Up @@ -348,6 +353,7 @@ impl<'de> Deserializer<'de> {
let doc = Bson::Symbol(symbol).into_extended_document(false);
visitor.visit_map(MapDeserializer::new(
doc,
#[allow(deprecated)]
DeserializerOptions::builder().human_readable(false).build(),
))
}
Expand Down Expand Up @@ -454,12 +460,19 @@ impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> {

self.deserialize_next(visitor, DeserializerHint::RawBson)
}
HUMAN_READABLE_NEWTYPE => {
let old = self.human_readable;
self.human_readable = true;
let result = visitor.visit_newtype_struct(&mut *self);
self.human_readable = old;
result
}
_ => visitor.visit_newtype_struct(self),
}
}

fn is_human_readable(&self) -> bool {
false
self.human_readable
}

forward_to_deserialize_any! {
Expand Down
12 changes: 11 additions & 1 deletion src/de/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::{
document::{Document, IntoIter},
oid::ObjectId,
raw::{RawBsonRef, RAW_ARRAY_NEWTYPE, RAW_BSON_NEWTYPE, RAW_DOCUMENT_NEWTYPE},
serde_helpers::HUMAN_READABLE_NEWTYPE,
spec::BinarySubtype,
uuid::UUID_NEWTYPE_NAME,
Binary,
Expand Down Expand Up @@ -578,6 +579,7 @@ pub struct Deserializer {
pub struct DeserializerOptions {
/// Whether the [`Deserializer`] should present itself as human readable or not.
/// The default is true.
#[deprecated = "use bson::serde_helpers::HumanReadable"]
pub human_readable: Option<bool>,
}

Expand All @@ -597,6 +599,8 @@ pub struct DeserializerOptionsBuilder {

impl DeserializerOptionsBuilder {
/// Set the value for [`DeserializerOptions::human_readable`].
#[deprecated = "use bson::serde_helpers::HumanReadable"]
#[allow(deprecated)]
pub fn human_readable(mut self, val: impl Into<Option<bool>>) -> Self {
self.options.human_readable = val.into();
self
Expand Down Expand Up @@ -716,6 +720,7 @@ macro_rules! forward_to_deserialize {
impl<'de> de::Deserializer<'de> for Deserializer {
type Error = crate::de::Error;

#[allow(deprecated)]
fn is_human_readable(&self) -> bool {
self.options.human_readable.unwrap_or(true)
}
Expand Down Expand Up @@ -815,7 +820,7 @@ impl<'de> de::Deserializer<'de> for Deserializer {

#[inline]
fn deserialize_newtype_struct<V>(
self,
mut self,
name: &'static str,
visitor: V,
) -> crate::de::Result<V::Value>
Expand Down Expand Up @@ -848,6 +853,11 @@ impl<'de> de::Deserializer<'de> for Deserializer {

self.deserialize_next(visitor, DeserializerHint::RawBson)
}
#[allow(deprecated)]
HUMAN_READABLE_NEWTYPE => {
self.options.human_readable = Some(true);
visitor.visit_newtype_struct(self)
}
_ => visitor.visit_newtype_struct(self),
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/ser/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use super::{write_binary, write_cstring, write_f64, write_i32, write_i64, write_
use crate::{
raw::{RAW_ARRAY_NEWTYPE, RAW_DOCUMENT_NEWTYPE},
ser::{Error, Result},
serde_helpers::HUMAN_READABLE_NEWTYPE,
spec::{BinarySubtype, ElementType},
uuid::UUID_NEWTYPE_NAME,
};
Expand All @@ -30,6 +31,8 @@ pub(crate) struct Serializer {

/// Hint provided by the type being serialized.
hint: SerializerHint,

human_readable: bool,
}

/// Various bits of information that the serialized type can provide to the serializer to
Expand Down Expand Up @@ -60,6 +63,7 @@ impl Serializer {
bytes: Vec::new(),
type_index: 0,
hint: SerializerHint::None,
human_readable: false,
}
}

Expand Down Expand Up @@ -115,7 +119,7 @@ impl<'a> serde::Serializer for &'a mut Serializer {
type SerializeStructVariant = VariantSerializer<'a>;

fn is_human_readable(&self) -> bool {
false
self.human_readable
}

#[inline]
Expand Down Expand Up @@ -267,6 +271,13 @@ impl<'a> serde::Serializer for &'a mut Serializer {
UUID_NEWTYPE_NAME => self.hint = SerializerHint::Uuid,
RAW_DOCUMENT_NEWTYPE => self.hint = SerializerHint::RawDocument,
RAW_ARRAY_NEWTYPE => self.hint = SerializerHint::RawArray,
HUMAN_READABLE_NEWTYPE => {
let old = self.human_readable;
self.human_readable = true;
let result = value.serialize(&mut *self);
self.human_readable = old;
return result;
}
_ => {}
}
value.serialize(self)
Expand Down
12 changes: 11 additions & 1 deletion src/ser/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
extjson,
oid::ObjectId,
raw::{RawDbPointerRef, RawRegexRef, RAW_ARRAY_NEWTYPE, RAW_DOCUMENT_NEWTYPE},
serde_helpers::HUMAN_READABLE_NEWTYPE,
spec::BinarySubtype,
uuid::UUID_NEWTYPE_NAME,
Binary,
Expand Down Expand Up @@ -120,6 +121,7 @@ pub struct Serializer {
pub struct SerializerOptions {
/// Whether the [`Serializer`] should present itself as human readable or not.
/// The default value is true.
#[deprecated = "use bson::serde_helpers::HumanReadable"]
pub human_readable: Option<bool>,
}

Expand All @@ -139,6 +141,8 @@ pub struct SerializerOptionsBuilder {

impl SerializerOptionsBuilder {
/// Set the value for [`SerializerOptions::is_human_readable`].
#[deprecated = "use bson::serde_helpers::HumanReadable"]
#[allow(deprecated)]
pub fn human_readable(mut self, value: impl Into<Option<bool>>) -> Self {
self.options.human_readable = value.into();
self
Expand Down Expand Up @@ -296,7 +300,7 @@ impl ser::Serializer for Serializer {

#[inline]
fn serialize_newtype_struct<T: ?Sized>(
self,
mut self,
name: &'static str,
value: &T,
) -> crate::ser::Result<Bson>
Expand Down Expand Up @@ -348,6 +352,11 @@ impl ser::Serializer for Serializer {
b
))),
},
#[allow(deprecated)]
HUMAN_READABLE_NEWTYPE => {
self.options.human_readable = Some(true);
value.serialize(self)
}
_ => value.serialize(self),
}
}
Expand Down Expand Up @@ -447,6 +456,7 @@ impl ser::Serializer for Serializer {
})
}

#[allow(deprecated)]
fn is_human_readable(&self) -> bool {
self.options.human_readable.unwrap_or(true)
}
Expand Down
45 changes: 43 additions & 2 deletions src/serde_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Collection of helper functions for serializing to and deserializing from BSON using Serde
use std::{convert::TryFrom, result::Result};
use std::{convert::TryFrom, marker::PhantomData, result::Result};

use serde::{ser, Serialize, Serializer};
use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer};

use crate::oid::ObjectId;

Expand Down Expand Up @@ -794,3 +794,44 @@ pub mod timestamp_as_u32 {
Ok(Timestamp { time, increment: 0 })
}
}

/// Wrapping a type in `HumanReadable` signals to the BSON serde integration that it and all
/// recursively contained types should be handled as if
/// [`SerializerOptions::human_readable`](crate::SerializerOptions::human_readable) and
/// [`DeserializerOptions::human_readable`](crate::DeserializerOptions::human_readable) are
/// set to `true`.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct HumanReadable<T>(pub T);

pub(crate) const HUMAN_READABLE_NEWTYPE: &str = "$__bson_private_human_readable";

impl<T: Serialize> Serialize for HumanReadable<T> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_newtype_struct(HUMAN_READABLE_NEWTYPE, &self.0)
}
}

impl<'de, T: Deserialize<'de>> Deserialize<'de> for HumanReadable<T> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct V<T>(PhantomData<fn() -> T>);
impl<'de, T: Deserialize<'de>> Visitor<'de> for V<T> {
type Value = HumanReadable<T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("HumanReadable wrapper")
}
fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(HumanReadable)
}
}
deserializer.deserialize_newtype_struct(HUMAN_READABLE_NEWTYPE, V(PhantomData))
}
}
1 change: 1 addition & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod binary_subtype;
mod datetime;
mod modules;
mod serde;
mod serde_helpers;
mod spec;

use modules::TestLock;
Expand Down
Loading

0 comments on commit 9c4b963

Please sign in to comment.