diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index c9472184..eb86578f 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -17,6 +17,12 @@ pub use chrono_datetime_as_bson_datetime::{ deserialize as deserialize_chrono_datetime_from_bson_datetime, serialize as serialize_chrono_datetime_as_bson_datetime, }; +#[cfg(feature = "chrono-0_4")] +#[doc(inline)] +pub use chrono_datetime_as_bson_datetime_optional::{ + deserialize as deserialize_chrono_datetime_from_bson_datetime_optional, + serialize as serialize_chrono_datetime_as_bson_datetime_optional, +}; #[doc(inline)] pub use hex_string_as_object_id::{ deserialize as deserialize_hex_string_from_object_id, @@ -307,6 +313,51 @@ pub mod chrono_datetime_as_bson_datetime { } } +/// Contains functions to serialize an [`Option`] as an +/// [`Option`] and deserialize an [`Option`] from an +/// [`Option`]. +/// +/// ```rust +/// # #[cfg(feature = "chrono-0_4")] +/// # { +/// # use serde::{Serialize, Deserialize}; +/// # use bson::serde_helpers::chrono_datetime_as_bson_datetime_optional; +/// #[derive(Serialize, Deserialize)] +/// struct Event { +/// #[serde(with = "chrono_datetime_as_bson_datetime_optional")] +/// pub date: Option>, +/// } +/// # } +/// ``` +#[cfg(feature = "chrono-0_4")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] +pub mod chrono_datetime_as_bson_datetime_optional { + use crate::DateTime; + use chrono::Utc; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::result::Result; + + /// Deserializes a [`chrono::DateTime`] from a [`crate::DateTime`]. + #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] + pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + let val = Option::deserialize(deserializer)?.map(|datetime: DateTime| datetime.to_chrono()); + Ok(val) + } + + /// Serializes a [`Option`] as a [`Option`]. + #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] + pub fn serialize( + val: &Option>, + serializer: S, + ) -> Result { + let datetime = val.map(DateTime::from_chrono); + datetime.serialize(serializer) + } +} + /// Contains functions to serialize an RFC 3339 (ISO 8601) formatted string as a [`crate::DateTime`] /// and deserialize an RFC 3339 (ISO 8601) formatted string from a [`crate::DateTime`]. /// diff --git a/src/tests/serde.rs b/src/tests/serde.rs index b89ad79e..1720d7dc 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -866,6 +866,59 @@ fn test_datetime_helpers() { assert_eq!(b.date, expected); } + #[cfg(feature = "chrono-0_4")] + { + use std::str::FromStr; + #[derive(Deserialize, Serialize)] + struct B { + #[serde(with = "serde_helpers::chrono_datetime_as_bson_datetime_optional")] + pub date: Option>, + } + + let date = r#" + { + "date": { + "$date": { + "$numberLong": "1591700287095" + } + } + }"#; + let json: serde_json::Value = serde_json::from_str(date).unwrap(); + let b: B = serde_json::from_value(json).unwrap(); + let expected: Option> = + Some(chrono::DateTime::from_str("2020-06-09 10:58:07.095 UTC").unwrap()); + assert_eq!(b.date, expected); + let doc = to_document(&b).unwrap(); + assert_eq!( + Some(doc.get_datetime("date").unwrap().to_chrono()), + expected + ); + let b: B = from_document(doc).unwrap(); + assert_eq!(b.date, expected); + } + + #[cfg(feature = "chrono-0_4")] + { + #[derive(Deserialize, Serialize)] + struct B { + #[serde(with = "serde_helpers::chrono_datetime_as_bson_datetime_optional")] + pub date: Option>, + } + + let date = r#" + { + "date": null + }"#; + let json: serde_json::Value = serde_json::from_str(date).unwrap(); + let b: B = serde_json::from_value(json).unwrap(); + let expected = None; + assert_eq!(b.date, expected); + let doc = to_document(&b).unwrap(); + assert_eq!(None, expected); + let b: B = from_document(doc).unwrap(); + assert_eq!(b.date, expected); + } + #[derive(Deserialize, Serialize)] struct C { #[serde(with = "rfc3339_string_as_bson_datetime")]