diff --git a/src/datetime.rs b/src/datetime.rs index 3fc7a20c..1ea6cb1f 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -108,21 +108,16 @@ use crate::error::{Error, Result}; /// /// ### `serde_helpers` /// The `bson` crate provides a number of useful helpers for serializing and deserializing -/// various datetime types to and from different formats using the [`serde_with`](https://docs.rs/serde_with/1.11.0/serde_with/) -/// crate. -/// -/// > **Note:** All helpers in this module require use of the [`#[serde_as]`](https://docs.rs/serde_with/latest/serde_with/attr.serde_as.html) -/// > attribute on the struct. This enables the enhanced serialization behavior provided by -/// > `serde_with-3`. -/// -/// For example, to serialize a [`chrono::DateTime`] as a BSON datetime, you can use +/// various datetime types to and from different formats. For example, to serialize a +/// [`chrono::DateTime`] as a BSON datetime, you can use /// [`crate::serde_helpers::datetime::FromChrono04DateTime`]. /// Similarly, to serialize a BSON [`DateTime`] to a string, you can use /// [`crate::serde_helpers::datetime::AsRfc3339String`]. Check out the -/// [`crate::serde_helpers`] module documentation for a list of all of the helpers -/// offered by the crate. +/// [`crate::serde_helpers`] module documentation for a list of all of the helpers offered by the +/// crate. +/// /// ```rust -/// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] +/// # #[cfg(feature = "chrono-0_4")] /// # { /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; @@ -143,26 +138,29 @@ use crate::error::{Error, Result}; /// chrono_as_bson: chrono::DateTime, /// /// // serializes as an RFC 3339 / ISO-8601 string. -/// // this requires the "serde_with-3" feature flag /// #[serde_as(as = "datetime::AsRfc3339String")] /// bson_as_string: bson::DateTime, /// } /// # } /// ``` -/// The main benefit of using the [`serde_with`](https://docs.rs/serde_with/1.11.0/serde_with/) crate -/// is that it can handle nested [`chrono::DateTime`] values (e.g. in [`Option`] or [`Vec`]). +/// ### The `serde_with-3` feature flag +/// +/// The `serde_with-3` feature can be enabled to support more ergonomic serde attributes for +/// (de)serializing [`chrono::DateTime`] from/to BSON via the [`serde_with`](https://docs.rs/serde_with/1.11.0/serde_with/) +/// crate. The main benefit of this compared to the regular `serde_helpers` is that `serde_with-3` +/// can handle nested [`chrono::DateTime`] values (e.g. in [`Option`]), whereas the former only +/// works on fields that are exactly [`chrono::DateTime`]. /// ``` /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { /// use serde::{Deserialize, Serialize}; -/// use serde_with::serde_as; -/// use bson::{doc, serde_helpers::datetime}; +/// use bson::doc; /// -/// #[serde_as] +/// #[serde_with::serde_as] /// #[derive(Deserialize, Serialize, PartialEq, Debug)] /// struct Foo { -/// /// serializes as a BSON datetime rather than using [`chrono::DateTime`]'s serialization -/// #[serde_as(as = "Option")] +/// /// Serializes as a BSON datetime rather than using [`chrono::DateTime`]'s serialization +/// #[serde_as(as = "Option")] /// as_bson: Option>, /// } /// diff --git a/src/extjson/models.rs b/src/extjson/models.rs index 88d3a2ca..cc806ef8 100644 --- a/src/extjson/models.rs +++ b/src/extjson/models.rs @@ -5,6 +5,7 @@ use serde::{ Deserialize, Serialize, }; +use serde_with::serde_as; use std::borrow::Cow; use crate::{ @@ -12,6 +13,7 @@ use crate::{ error::{Error, Result}, oid, raw::serde::CowStr, + serde_helpers::u32, spec::BinarySubtype, Bson, }; @@ -215,12 +217,13 @@ pub(crate) struct Timestamp { } #[derive(Serialize, Deserialize, Debug)] +#[serde_as] #[serde(deny_unknown_fields)] pub(crate) struct TimestampBody { - #[serde(serialize_with = "crate::serde_helpers::serialize_u32_as_i64")] + #[serde_as(as = "u32::AsI64")] pub(crate) t: u32, - #[serde(serialize_with = "crate::serde_helpers::serialize_u32_as_i64")] + #[serde_as(as = "u32::AsI64")] pub(crate) i: u32, } diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index ded62da6..d6a798bc 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,27 +1,11 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde -use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; use std::{ - convert::TryFrom, marker::PhantomData, ops::{Deref, DerefMut}, result::Result, }; -#[doc(inline)] -pub use timestamp_as_u32::{ - deserialize as deserialize_timestamp_from_u32, - serialize as serialize_timestamp_as_u32, -}; -#[doc(inline)] -pub use u32_as_f64::{deserialize as deserialize_u32_from_f64, serialize as serialize_u32_as_f64}; -#[doc(inline)] -pub use u32_as_timestamp::{ - deserialize as deserialize_u32_from_timestamp, - serialize as serialize_u32_as_timestamp, -}; -#[doc(inline)] -pub use u64_as_f64::{deserialize as deserialize_u64_from_f64, serialize as serialize_u64_as_f64}; - #[cfg(feature = "uuid-1")] #[doc(inline)] pub use uuid_1_as_binary::{ @@ -47,35 +31,6 @@ pub use uuid_1_as_python_legacy_binary::{ serialize as serialize_uuid_1_as_python_legacy_binary, }; -/// Attempts to serialize a u32 as an i32. Errors if an exact conversion is not possible. -pub fn serialize_u32_as_i32(val: &u32, serializer: S) -> Result { - match i32::try_from(*val) { - Ok(val) => serializer.serialize_i32(val), - Err(_) => Err(ser::Error::custom(format!("cannot convert {} to i32", val))), - } -} - -/// Serializes a u32 as an i64. -pub fn serialize_u32_as_i64(val: &u32, serializer: S) -> Result { - serializer.serialize_i64(*val as i64) -} - -/// Attempts to serialize a u64 as an i32. Errors if an exact conversion is not possible. -pub fn serialize_u64_as_i32(val: &u64, serializer: S) -> Result { - match i32::try_from(*val) { - Ok(val) => serializer.serialize_i32(val), - Err(_) => Err(ser::Error::custom(format!("cannot convert {} to i32", val))), - } -} - -/// Attempts to serialize a u64 as an i64. Errors if an exact conversion is not possible. -pub fn serialize_u64_as_i64(val: &u64, serializer: S) -> Result { - match i64::try_from(*val) { - Ok(val) => serializer.serialize_i64(val), - Err(_) => Err(ser::Error::custom(format!("cannot convert {} to i64", val))), - } -} - #[cfg(feature = "serde_with-3")] pub mod object_id { use crate::{macros::serde_conv_doc, oid::ObjectId}; @@ -138,87 +93,6 @@ pub mod object_id { ); } -/// Contains functions to serialize a u32 as an f64 (BSON double) and deserialize a -/// u32 from an f64 (BSON double). -/// -/// ```rust -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::u32_as_f64; -/// #[derive(Serialize, Deserialize)] -/// struct FileInfo { -/// #[serde(with = "u32_as_f64")] -/// pub size_bytes: u32, -/// } -/// ``` -pub mod u32_as_f64 { - use serde::{de, Deserialize, Deserializer, Serializer}; - - /// Deserializes a u32 from an f64 (BSON double). Errors if an exact conversion is not possible. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let f = f64::deserialize(deserializer)?; - if (f - f as u32 as f64).abs() <= f64::EPSILON { - Ok(f as u32) - } else { - Err(de::Error::custom(format!( - "cannot convert f64 (BSON double) {} to u32", - f - ))) - } - } - - /// Serializes a u32 as an f64 (BSON double). - pub fn serialize(val: &u32, serializer: S) -> Result { - serializer.serialize_f64(*val as f64) - } -} - -/// Contains functions to serialize a u64 as an f64 (BSON double) and deserialize a -/// u64 from an f64 (BSON double). -/// -/// ```rust -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::u64_as_f64; -/// #[derive(Serialize, Deserialize)] -/// struct FileInfo { -/// #[serde(with = "u64_as_f64")] -/// pub size_bytes: u64, -/// } -/// ``` -pub mod u64_as_f64 { - use serde::{de, ser, Deserialize, Deserializer, Serializer}; - - /// Deserializes a u64 from an f64 (BSON double). Errors if an exact conversion is not possible. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let f = f64::deserialize(deserializer)?; - if (f - f as u64 as f64).abs() <= f64::EPSILON { - Ok(f as u64) - } else { - Err(de::Error::custom(format!( - "cannot convert f64 (BSON double) {} to u64", - f - ))) - } - } - - /// Serializes a u64 as an f64 (BSON double). Errors if an exact conversion is not possible. - pub fn serialize(val: &u64, serializer: S) -> Result { - if val < &u64::MAX && *val == *val as f64 as u64 { - serializer.serialize_f64(*val as f64) - } else { - Err(ser::Error::custom(format!( - "cannot convert u64 {} to f64 (BSON double)", - val - ))) - } - } -} - /// Type converters for serializing and deserializing [`crate::DateTime`] using /// [`serde_with::serde_as`]. /// @@ -238,7 +112,6 @@ pub mod datetime { use chrono::Utc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Converts a [`DateTime`] to and from an RFC 3339 (ISO 8601) formatted string. @@ -380,6 +253,282 @@ pub mod datetime { ); } +/// Type converters for serializing and deserializing `u32` using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`u32::FromTimestamp`] — converts a [`crate::Timestamp`] to and from a `u32`. +/// - [`u32::AsTimestamp`] — converts a `u32` to and from a [`crate::Timestamp`]. +/// - [`u32::AsF64`] — converts a `u32` to and from an `f64`. +/// - [`u32::AsI32`] — converts a `u32` to and from an `i32`. +/// - [`u32::AsI64`] — converts a `u32` to and from an `i64`. +#[cfg(feature = "serde_with-3")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] +pub mod u32 { + use crate::{macros::serde_conv_doc, Timestamp}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + serde_conv_doc!( + /// Converts a [`Timestamp`] to and from a `u32`. + /// + /// The `u32` should represent seconds since the Unix epoch. + /// + /// Serialization errors if the Timestamp has a non-zero increment. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::{serde_helpers::datetime, DateTime}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "u32::FromTimestamp")] + /// pub timestamp: Timestamp, + /// } + /// # } + /// ``` + pub FromTimestamp, + Timestamp, + |timestamp: &Timestamp| -> Result { + if timestamp.increment != 0 { + return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", timestamp)); + } + Ok(timestamp.time) + }, + |value: u32| -> Result { + Ok(Timestamp { time: value, increment: 0 }) + } + ); + + serde_conv_doc!( + /// Converts a `u32` to and from a [`Timestamp`]. + /// + /// The `u32` should represent seconds since the Unix epoch. + /// + /// Deserialization errors if the Timestamp has a non-zero increment. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::serde_helpers::u32; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde_as(as = "u32::AsTimestamp")] + /// pub time: u32, + /// } + /// # } + /// ``` + pub AsTimestamp, + u32, + |value: &u32| -> Result { + Ok(Timestamp { time: *value, increment: 0 }) + }, + |timestamp: Timestamp| -> Result { + if timestamp.increment != 0 { + return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", timestamp)); + } + Ok(timestamp.time) + } + ); + + serde_conv_doc!( + /// Converts a `u32` to and from an `f64`. + /// + /// Deserialization errors if an exact conversion is not possible. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::serde_helpers::u32; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct FileInfo { + /// #[serde_as(as = "u32::AsF64")] + /// pub size_bytes: u32, + /// } + /// # } + /// ``` + pub AsF64, + u32, + |value: &u32| -> Result { + Ok(*value as f64) + }, + |value: f64| -> Result { + if (value - value as u32 as f64).abs() <= f64::EPSILON { + Ok(value as u32) + } else { + Err(format!("Cannot convert f64 {} to u32", value)) + } + } + ); + + serde_conv_doc!( + /// Converts a `u32` to and from an `i32`. + /// + /// Errors if an exact conversion is not possible. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::{serde_helpers::u32}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "u32::AsI32")] + /// pub value: u32, + /// } + /// # } + /// ``` + pub AsI32, + u32, + |value: &u32| -> Result { + i32::try_from(*value).map_err(|e| format!("Cannot convert u32 {} to i32: {}", value, e)) + }, + |value: i32| -> Result { + u32::try_from(value).map_err(|e| format!("Cannot convert i32 {} to u32: {}", value, e)) + } + ); + + serde_conv_doc!( + /// Converts a `u32` to and from an `i64`. + /// + /// Deserialization errors if an exact conversion is not possible. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::{serde_helpers::u32}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "u32::AsI64")] + /// pub value: u32, + /// } + /// # } + /// ``` + pub AsI64, + u32, + |value: &u32| -> Result { + Ok(*value as i64) + }, + |value: i64| -> Result { + u32::try_from(value).map_err(|e| format!("Cannot convert i64 {} to u32: {}", value, e)) + } + ); +} + +/// Type converters for serializing and deserializing `u64` using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`u64::AsF64`] — converts a `u64` to and from an `f64`. +/// - [`u64::AsI32`] — converts a `u64` to and from an `i32`. +/// - [`u64::AsI64`] — converts a `u64` to and from an `i64`. +#[cfg(feature = "serde_with-3")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] +pub mod u64 { + use crate::macros::serde_conv_doc; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + serde_conv_doc!( + /// Converts a `u64` to and from an `f64`. + /// + /// Errors if an exact conversion is not possible. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::serde_helpers::u64; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct FileInfo { + /// #[serde_as(as = "u64::AsF64")] + /// pub size_bytes: u64, + /// } + /// # } + /// ``` + pub AsF64, + u64, + |value: &u64| -> Result { + if value < &u64::MAX && *value == *value as f64 as u64 { + Ok(*value as f64) + } else { + Err(format!("Cannot convert u64 {} to f64", value)) + } + }, + |value: f64| -> Result { + if (value - value as u64 as f64).abs() <= f64::EPSILON { + Ok(value as u64) + } else { + Err(format!("Cannot convert f64 {} to u64", value)) + } + } + ); + + serde_conv_doc!( + /// Converts a `u64` to and from an `i32`. + /// + /// Errors if an exact conversion is not possible. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::{serde_helpers::u64}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "u64::AsI32")] + /// pub value: u64, + /// } + /// # } + /// ``` + pub AsI32, + u64, + |value: &u64| -> Result { + i32::try_from(*value).map_err(|e| format!("Cannot convert u64 {} to i32: {}", value, e)) + }, + |value: i32| -> Result { + u64::try_from(value).map_err(|e| format!("Cannot convert i32 {} to u64: {}", value, e)) + } + ); + + serde_conv_doc!( + /// Converts a `u64` to and from an `i64`. + /// + /// Errors if an exact conversion is not possible. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::{serde_helpers::u64}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "u64::AsI64")] + /// pub value: u64, + /// } + /// # } + /// ``` + pub AsI64, + u64, + |value: &u64| -> Result { + i64::try_from(*value).map_err(|e| format!("Cannot convert u64 {} to i64: {}", value, e)) + }, + |value: i64| -> Result { + u64::try_from(value).map_err(|e| format!("Cannot convert i64 {} to u64: {}", value, e)) + } + ); +} + #[allow(unused_macros)] macro_rules! as_binary_mod { ($feat:meta, $uu:path) => { @@ -542,81 +691,6 @@ pub mod uuid_1_as_c_sharp_legacy_binary { ); } -/// Contains functions to serialize a u32 as a bson::Timestamp and deserialize a u32 from a -/// bson::Timestamp. The u32 should represent seconds since the Unix epoch. -/// -/// ```rust -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::u32_as_timestamp; -/// #[derive(Serialize, Deserialize)] -/// struct Event { -/// #[serde(with = "u32_as_timestamp")] -/// pub time: u32, -/// } -/// ``` -pub mod u32_as_timestamp { - use crate::{Bson, Timestamp}; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use std::result::Result; - - /// Serializes a u32 as a bson::Timestamp. - pub fn serialize(val: &u32, serializer: S) -> Result { - let timestamp = Bson::Timestamp(Timestamp { - time: *val, - increment: 0, - }); - timestamp.serialize(serializer) - } - - /// Deserializes a u32 from a bson::Timestamp. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let timestamp = Timestamp::deserialize(deserializer)?; - Ok(timestamp.time) - } -} - -/// Contains functions to serialize a bson::Timestamp as a u32 and deserialize a bson::Timestamp -/// from a u32. The u32 should represent seconds since the Unix epoch. Serialization will return an -/// error if the Timestamp has a non-zero increment. -/// -/// ```rust -/// # use serde::{Serialize, Deserialize}; -/// # use bson::{serde_helpers::timestamp_as_u32, Timestamp}; -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "timestamp_as_u32")] -/// pub timestamp: Timestamp, -/// } -/// ``` -pub mod timestamp_as_u32 { - use crate::Timestamp; - use serde::{ser, Deserialize, Deserializer, Serializer}; - use std::result::Result; - - /// Serializes a bson::Timestamp as a u32. Returns an error if the conversion is lossy (i.e. the - /// Timestamp has a non-zero increment). - pub fn serialize(val: &Timestamp, serializer: S) -> Result { - if val.increment != 0 { - return Err(ser::Error::custom( - "Cannot convert Timestamp with a non-zero increment to u32", - )); - } - serializer.serialize_u32(val.time) - } - - /// Deserializes a bson::Timestamp from a u32. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let time = u32::deserialize(deserializer)?; - 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 serialized to and deserialized from their human-readable /// formats. diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 1bbde432..32b0a5bf 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -7,7 +7,7 @@ use crate::{ deserialize_from_document, doc, oid::ObjectId, - serde_helpers::{self, datetime, object_id, timestamp_as_u32, u32_as_timestamp}, + serde_helpers::{self, datetime, object_id, u32, u64}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -652,122 +652,909 @@ fn test_serialize_deserialize_unsigned_numbers() { } #[test] -fn test_unsigned_helpers() { +#[cfg(feature = "serde_with-3")] +fn test_datetime_rfc3339_string_helpers() { + let _guard = LOCK.run_concurrently(); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::AsRfc3339String")] + pub date: DateTime, + + #[serde_as(as = "Option")] + pub date_optional_none: Option, + #[serde_as(as = "Option")] + pub date_optional_none: Option, + + #[serde_as(as = "Option")] + pub date_optional_some: Option, + #[serde_as(as = "Option")] + pub date_optional_some: Option, + + #[serde_as(as = "Vec")] + pub date_vector: Vec, + } + #[serde_as(as = "Vec")] + pub date_vector: Vec, + } + + let iso = "1996-12-20T00:39:57Z"; + let date = DateTime::parse_rfc3339_str(iso).unwrap(); + let a = A { + date, + date_optional_none: None, + date_optional_some: Some(date), + date_vector: vec![date], + }; + let iso = "1996-12-20T00:39:57Z"; + let date = DateTime::parse_rfc3339_str(iso).unwrap(); + let a = A { + date, + date_optional_none: None, + date_optional_some: Some(date), + date_vector: vec![date], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_str("date").unwrap(), + iso, + "Expected serialized date to match original date from RFC 3339 string." + ); + // Validate serialized data + assert_eq!( + doc.get_str("date").unwrap(), + iso, + "Expected serialized date to match original date from RFC 3339 string." + ); + + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::String(iso.to_string())), + "Expected serialized date_optional_some to match original." + ); + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::String(iso.to_string())), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::String(date.try_to_rfc3339_string().unwrap())]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector to match the original." + ); + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::String(date.try_to_rfc3339_string().unwrap())]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector to match the original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); + + // Validate deserializing error case with an invalid DateTime string + let invalid_doc = doc! { + "date": "not_a_valid_date", + "date_optional_none": Bson::Null, + "date_optional_some": "also_invalid_date", + "date_vector": ["bad1", "bad2"] + }; + let result: Result = deserialize_from_document(invalid_doc); + assert!( + result.is_err(), + "Deserialization should fail for invalid DateTime strings" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", + err_string + ); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "datetime::FromRfc3339String")] + pub date: String, + + #[serde_as(as = "Option")] + pub date_optional_none: Option, + + #[serde_as(as = "Option")] + pub date_optional_some: Option, + + #[serde_as(as = "Vec")] + pub date_vector: Vec, + } + + let date = DateTime::now(); + let b = B { + date: date.try_to_rfc3339_string().unwrap(), + date_optional_none: None, + date_optional_some: Some(date.try_to_rfc3339_string().unwrap()), + date_vector: vec![date.try_to_rfc3339_string().unwrap()], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); + + // Validate serialized data + assert_eq!( + *doc.get_datetime("date").unwrap(), + date, + "Expected serialized date to be a BSON DateTime." + ); + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); + + // Validate deserializing error case with an invalid DateTime string + let invalid_doc = doc! { + "date": "not_a_valid_date", + "date_optional_none": Bson::Null, + "date_optional_some": "also_invalid_date", + "date_vector": ["bad1", "bad2"] + }; + let result: Result = deserialize_from_document(invalid_doc); + assert!( + result.is_err(), + "Deserialization should fail for invalid DateTime strings" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", + err_string + ); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "datetime::FromRfc3339String")] + pub date: String, + + #[serde_as(as = "Option")] + pub date_optional_none: Option, + + #[serde_as(as = "Option")] + pub date_optional_some: Option, + + #[serde_as(as = "Vec")] + pub date_vector: Vec, + } + + let date = DateTime::now(); + let b = B { + date: date.try_to_rfc3339_string().unwrap(), + date_optional_none: None, + date_optional_some: Some(date.try_to_rfc3339_string().unwrap()), + date_vector: vec![date.try_to_rfc3339_string().unwrap()], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); + + // Validate serialized data + assert_eq!( + *doc.get_datetime("date").unwrap(), + date, + "Expected serialized date to be a BSON DateTime." + ); + + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(date)), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector match the original." + ); + + // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "Deserialized struct does not match original." + ); + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(date)), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector match the original." + ); + + // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "Deserialized struct does not match original." + ); + + // Validate serializing error case with an invalid DateTime string + let invalid_date = "invalid_date"; + let bad_b = B { + date: invalid_date.to_string(), + date_optional_none: None, + date_optional_some: Some(invalid_date.to_string()), + date_vector: vec![invalid_date.to_string()], + }; + let result = serialize_to_document(&bad_b); + assert!( + result.is_err(), + "Serialization should fail for invalid DateTime strings" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", + err_string + ); +} + // Validate serializing error case with an invalid DateTime string + let invalid_date = "invalid_date"; + let bad_b = B { + date: invalid_date.to_string(), + date_optional_none: None, + date_optional_some: Some(invalid_date.to_string()), + date_vector: vec![invalid_date.to_string()], + }; + let result = serialize_to_document(&bad_b); + assert!( + result.is_err(), + "Serialization should fail for invalid DateTime strings" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", + err_string + ); +} + +#[test] +#[cfg(feature = "serde_with-3")] +fn test_datetime_i64_helper() { + let _guard = LOCK.run_concurrently(); +#[test] +#[cfg(feature = "serde_with-3")] +fn test_datetime_i64_helper() { + let _guard = LOCK.run_concurrently(); + + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromI64")] + date: i64, + + #[serde_as(as = "Option")] + date_optional_none: Option, + + #[serde_as(as = "Option")] + date_optional_some: Option, + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromI64")] + date: i64, + + #[serde_as(as = "Option")] + date_optional_none: Option, + + #[serde_as(as = "Option")] + date_optional_some: Option, + + #[serde_as(as = "Vec")] + date_vector: Vec, + } + + let date = DateTime::now(); + let a = A { + date: date.timestamp_millis(), + date_optional_none: None, + date_optional_some: Some(date.timestamp_millis()), + date_vector: vec![date.timestamp_millis()], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap(), + &date, + "Expected serialized date to match original date." + ); + + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(date)), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector match the original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); +} + #[serde_as(as = "Vec")] + date_vector: Vec, + } + + let date = DateTime::now(); + let a = A { + date: date.timestamp_millis(), + date_optional_none: None, + date_optional_some: Some(date.timestamp_millis()), + date_vector: vec![date.timestamp_millis()], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap(), + &date, + "Expected serialized date to match original date." + ); + + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(date)), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector match the original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); +} + +#[test] +#[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] +fn test_datetime_chrono04_datetime_helper() { + let _guard = LOCK.run_concurrently(); + + use std::str::FromStr; +#[test] +#[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] +fn test_datetime_chrono04_datetime_helper() { + let _guard = LOCK.run_concurrently(); + + use std::str::FromStr; + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromChrono04DateTime")] + pub date: chrono::DateTime, + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromChrono04DateTime")] + pub date: chrono::DateTime, + + #[serde_as(as = "Option")] + pub date_optional_none: Option>, + #[serde_as(as = "Option")] + pub date_optional_none: Option>, + + #[serde_as(as = "Option")] + pub date_optional_some: Option>, + #[serde_as(as = "Option")] + pub date_optional_some: Option>, + + #[serde_as(as = "Vec")] + pub date_vector: Vec>, + } + #[serde_as(as = "Vec")] + pub date_vector: Vec>, + } + + let iso = "1996-12-20T00:39:57Z"; + let date: chrono::DateTime = chrono::DateTime::from_str(iso).unwrap(); + let a: A = A { + date, + date_optional_none: None, + date_optional_some: Some(date), + date_vector: vec![date], + }; + let iso = "1996-12-20T00:39:57Z"; + let date: chrono::DateTime = chrono::DateTime::from_str(iso).unwrap(); + let a: A = A { + date, + date_optional_none: None, + date_optional_some: Some(date), + date_vector: vec![date], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap().to_chrono(), + date, + "Expected serialized date to match original date." + ); + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap().to_chrono(), + date, + "Expected serialized date to match original date." + ); + + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(DateTime::from_chrono(date))), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::DateTime(date.into())]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector to be a BSON DateTime matching the \ + original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); +} + +#[test] +#[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] +fn test_datetime_time03_offset_datetime_helper() { + use time::OffsetDateTime; let _guard = LOCK.run_concurrently(); - #[derive(Serialize)] + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde(serialize_with = "serde_helpers::serialize_u32_as_i32")] - num_1: u32, - #[serde(serialize_with = "serde_helpers::serialize_u64_as_i32")] - num_2: u64, + #[serde_as(as = "datetime::FromTime03OffsetDateTime")] + pub date: OffsetDateTime, + + #[serde_as(as = "Option")] + pub date_optional_none: Option, + + #[serde_as(as = "Option")] + pub date_optional_some: Option, + + #[serde_as(as = "Vec")] + pub date_vector: Vec, } - let a = A { num_1: 1, num_2: 2 }; + let date = DateTime::now(); + let a: A = A { + date: date.to_time_0_3(), + date_optional_none: None, + date_optional_some: Some(date.to_time_0_3()), + date_vector: vec![date.to_time_0_3()], + }; + + // Serialize the struct to BSON let doc = serialize_to_document(&a).unwrap(); - assert!(doc.get_i32("num_1").unwrap() == 1); - assert!(doc.get_i32("num_2").unwrap() == 2); + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap(), + &date, + "Expected serialized date to match original date." + ); + + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); + + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(date)), + "Expected serialized date_optional_some to match original." + ); + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector to be a BSON DateTime matching the \ + original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); +} + +#[test] +#[cfg(feature = "serde_with-3")] +fn test_u32_timestamp_helpers() { + let _guard = LOCK.run_concurrently(); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "u32::FromTimestamp")] + pub timestamp: Timestamp, + + #[serde_as(as = "Option")] + pub timestamp_optional_none: Option, + + #[serde_as(as = "Option")] + pub timestamp_optional_some: Option, + + #[serde_as(as = "Vec")] + pub timestamp_vector: Vec, + } + + let time = 12345; + let timestamp = Timestamp { time, increment: 0 }; let a = A { - num_1: u32::MAX, - num_2: 1, + timestamp, + timestamp_optional_none: None, + timestamp_optional_some: Some(timestamp), + timestamp_vector: vec![timestamp], }; - let doc_result = serialize_to_document(&a); - assert!(doc_result.is_err()); - let a = A { - num_1: 1, - num_2: u64::MAX, + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get("timestamp").unwrap(), + &Bson::Int64(time as i64), + "Expected serialized time to match the original." + ); + + assert_eq!( + doc.get("timestamp_optional_none"), + Some(&Bson::Null), + "Expected serialized timestamp_optional_none to be None." + ); + + assert_eq!( + doc.get("timestamp_optional_some"), + Some(&Bson::Int64(time as i64)), + "Expected serialized timestamp_optional_some to match original time." + ); + + let timestamp_vector = doc + .get_array("timestamp_vector") + .expect("Expected serialized timestamp_vector to be a BSON array."); + let expected_timestamp_vector: Vec = vec![Bson::Int64(time as i64)]; + assert_eq!( + timestamp_vector, &expected_timestamp_vector, + "Expected each serialized element in timestamp_vector to match the original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); + + // Validate serializing error case with an invalid Timestamp + let invalid_timestamp_for_serializing = Timestamp { + time: 0, + increment: 2, }; - let doc_result = serialize_to_document(&a); - assert!(doc_result.is_err()); + let bad_a: A = A { + timestamp: invalid_timestamp_for_serializing, + timestamp_optional_none: None, + timestamp_optional_some: Some(invalid_timestamp_for_serializing), + timestamp_vector: vec![invalid_timestamp_for_serializing], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Serialization should fail for Timestamp with increment != 0" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert Timestamp with a non-zero increment to u32"), + "Expected error message to mention non-zero increment: {}", + err_string + ); - #[derive(Serialize)] + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct B { - #[serde(serialize_with = "serde_helpers::serialize_u32_as_i64")] - num_1: u32, - #[serde(serialize_with = "serde_helpers::serialize_u64_as_i64")] - num_2: u64, + #[serde_as(as = "u32::AsTimestamp")] + pub time: u32, + + #[serde_as(as = "Option")] + pub time_optional_none: Option, + + #[serde_as(as = "Option")] + pub time_optional_some: Option, + + #[serde_as(as = "Vec")] + pub time_vector: Vec, } + let time = 12345; let b = B { - num_1: u32::MAX, - num_2: i64::MAX as u64, + time, + time_optional_none: None, + time_optional_some: Some(time), + time_vector: vec![time], }; + + // Serialize the struct to BSON let doc = serialize_to_document(&b).unwrap(); - assert!(doc.get_i64("num_1").unwrap() == u32::MAX as i64); - assert!(doc.get_i64("num_2").unwrap() == i64::MAX); - let b = B { - num_1: 1, - num_2: i64::MAX as u64 + 1, + // Validate serialized data + assert_eq!( + doc.get_timestamp("time").unwrap(), + Timestamp { time, increment: 0 }, + "Expected serialized time to match the original." + ); + + assert_eq!( + doc.get("time_optional_none"), + Some(&Bson::Null), + "Expected serialized time_optional_none to be None." + ); + + assert_eq!( + doc.get("time_optional_some"), + Some(&Bson::Timestamp(Timestamp { time, increment: 0 })), + "Expected serialized time_optional_some to match original." + ); + + let time_vector = doc + .get_array("time_vector") + .expect("Expected serialized time_vector to be a BSON array."); + let expected_time_vector: Vec = vec![Bson::Timestamp(Timestamp { time, increment: 0 })]; + assert_eq!( + time_vector, &expected_time_vector, + "Expected each serialized element in time_vector to match the original." + ); + + // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "Deserialized struct does not match original." + ); + + // Validate deserializing error case with an invalid Timestamp + let invalid_timestamp_for_deserializing = Timestamp { + time: 0, + increment: 2, }; - let doc_result = serialize_to_document(&b); - assert!(doc_result.is_err()); + let invalid_doc = doc! { + "time": invalid_timestamp_for_deserializing, + "time_optional_none": Bson::Null, + "time_optional_some": Some(invalid_timestamp_for_deserializing), + "time_vector": [invalid_timestamp_for_deserializing] + }; + let result: Result = deserialize_from_document(invalid_doc); + assert!( + result.is_err(), + "Deserialization should fail for Timestamp with increment != 0" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert Timestamp with a non-zero increment to u32"), + "Expected error message to mention non-zero increment: {}", + err_string + ); +} + +#[test] +#[cfg(feature = "serde_with-3")] +fn test_u32_f64_helper() { + let _guard = LOCK.run_concurrently(); + #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct F { - #[serde(with = "serde_helpers::u32_as_f64")] - num_1: u32, - #[serde(with = "serde_helpers::u64_as_f64")] - num_2: u64, + struct A { + #[serde_as(as = "u32::AsF64")] + pub value: u32, + + #[serde_as(as = "Option")] + pub value_optional_none: Option, + + #[serde_as(as = "Option")] + pub value_optional_some: Option, + + #[serde_as(as = "Vec")] + pub value_vector: Vec, } - let f = F { - num_1: 101, - num_2: 12345, + let value = 12345; + let a = A { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; - let doc = serialize_to_document(&f).unwrap(); - assert!((doc.get_f64("num_1").unwrap() - 101.0).abs() < f64::EPSILON); - assert!((doc.get_f64("num_2").unwrap() - 12345.0).abs() < f64::EPSILON); - let back: F = deserialize_from_document(doc).unwrap(); - assert_eq!(back, f); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - let f = F { - num_1: 1, - // f64 cannot represent many large integers exactly, u64::MAX included - num_2: u64::MAX, - }; - let doc_result = serialize_to_document(&f); - assert!(doc_result.is_err()); + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); - let f = F { - num_1: 1, - num_2: u64::MAX - 255, - }; - let doc_result = serialize_to_document(&f); - assert!(doc_result.is_err()); + assert_eq!( + doc.get("value_optional_none"), + Some(&Bson::Null), + "Expected serialized value_optional_none to be None." + ); + + assert_eq!( + doc.get("value_optional_some"), + Some(&Bson::Double(value as f64)), + "Expected serialized value_optional_some to match original." + ); + + let value_vector = doc + .get_array("value_vector") + .expect("Expected serialized value_vector to be a BSON array."); + let expected_value_vector: Vec = vec![Bson::Double(value as f64)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); + + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + assert_eq!( + a_deserialized, a, + "Deserialized struct does not match original." + ); } #[test] #[cfg(feature = "serde_with-3")] -fn test_datetime_rfc3339_string_helpers() { +fn test_u32_i32_helper() { let _guard = LOCK.run_concurrently(); #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[derive(Deserialize, Serialize, PartialEq, Debug)] struct A { - #[serde_as(as = "datetime::AsRfc3339String")] - pub date: DateTime, + #[serde_as(as = "u32::AsI32")] + value: u32, - #[serde_as(as = "Option")] - pub date_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub date_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub date_vector: Vec, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let iso = "1996-12-20T00:39:57Z"; - let date = DateTime::parse_rfc3339_str(iso).unwrap(); + let value = 1; let a = A { - date, - date_optional_none: None, - date_optional_some: Some(date), - date_vector: vec![date], + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; // Serialize the struct to BSON @@ -775,30 +1562,30 @@ fn test_datetime_rfc3339_string_helpers() { // Validate serialized data assert_eq!( - doc.get_str("date").unwrap(), - iso, - "Expected serialized date to match original date from RFC 3339 string." + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." ); assert_eq!( - doc.get("date_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized date_optional_none to be None." + "Expected serialized value_optional_none to be None." ); assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::String(iso.to_string())), - "Expected serialized date_optional_some to match original." + doc.get("value_optional_some"), + Some(&Bson::Int32(value as i32)), + "Expected serialized value_optional_some to match original." ); - let date_vector = doc - .get_array("date_vector") - .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = vec![Bson::String(date.try_to_rfc3339_string().unwrap())]; + let value_vector = doc + .get_array("value_vector") + .expect("Expected serialized value_vector to be a BSON array."); + let expected_value_vector: Vec = vec![Bson::Int32(value as i32)]; assert_eq!( - date_vector, &expected_date_vector, - "Expected each serialized element in date_vector to match the original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); // Validate deserialized data @@ -808,135 +1595,164 @@ fn test_datetime_rfc3339_string_helpers() { "Deserialized struct does not match original." ); - // Validate deserializing error case with an invalid DateTime string - let invalid_doc = doc! { - "date": "not_a_valid_date", - "date_optional_none": Bson::Null, - "date_optional_some": "also_invalid_date", - "date_vector": ["bad1", "bad2"] + // Validate serialization fails because u32::MAX is too large to fit in i32 + let invalid_value_for_serializing = u32::MAX; + let bad_a: A = A { + value: invalid_value_for_serializing, + value_optional_none: None, + value_optional_some: Some(invalid_value_for_serializing), + value_vector: vec![invalid_value_for_serializing], }; - let result: Result = deserialize_from_document(invalid_doc); + let result = serialize_to_document(&bad_a); assert!( result.is_err(), - "Deserialization should fail for invalid DateTime strings" + "Serialization should fail for u32::MAX since it can't be exactly represented as i32" ); let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("BSON error"), - "Expected error message to mention BSON error: {}", + err_string.contains("Cannot convert u32"), + "Expected error message to mention failed u32 to i32 conversion, got: {}", + err_string + ); + + // Validate deserialization fails for i32::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i32::MIN; + let bad_a = doc! { + "value": invalid_value_for_deserializing, + "value_optional_none": Bson::Null, + "value_optional_some": Some(invalid_value_for_deserializing), + "value_vector": [invalid_value_for_deserializing], + }; + let result: Result = deserialize_from_document(bad_a); + assert!( + result.is_err(), + "Deserialization should fail for i32::MIN since it can't be exactly represented as u32" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert i32"), + "Expected error message to mention failed i32 to u32 conversion, got: {}", err_string ); +} + +#[test] +#[cfg(feature = "serde_with-3")] +fn test_u32_i64_helper() { + let _guard = LOCK.run_concurrently(); #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct B { - #[serde_as(as = "datetime::FromRfc3339String")] - pub date: String, + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct A { + #[serde_as(as = "u32::AsI64")] + value: u32, - #[serde_as(as = "Option")] - pub date_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub date_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub date_vector: Vec, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let date = DateTime::now(); - let b = B { - date: date.try_to_rfc3339_string().unwrap(), - date_optional_none: None, - date_optional_some: Some(date.try_to_rfc3339_string().unwrap()), - date_vector: vec![date.try_to_rfc3339_string().unwrap()], + let value = u32::MAX; + let a = A { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; // Serialize the struct to BSON - let doc = serialize_to_document(&b).unwrap(); + let doc = serialize_to_document(&a).unwrap(); // Validate serialized data assert_eq!( - *doc.get_datetime("date").unwrap(), - date, - "Expected serialized date to be a BSON DateTime." + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." ); assert_eq!( - doc.get("date_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized date_optional_none to be None." + "Expected serialized value_optional_none to be None." ); assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(date)), - "Expected serialized date_optional_some to match original." + doc.get("value_optional_some"), + Some(&Bson::Int64(value as i64)), + "Expected serialized value_optional_some to match original." ); - let date_vector = doc - .get_array("date_vector") - .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + let value_vector = doc + .get_array("value_vector") + .expect("Expected serialized value_vector to be a BSON array."); + let expected_value_vector: Vec = vec![Bson::Int64(value as i64)]; assert_eq!( - date_vector, &expected_date_vector, - "Expected each serialized element in date_vector match the original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); // Validate deserialized data - let b_deserialized: B = deserialize_from_document(doc).unwrap(); + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized, b, - "Deserialized struct does not match original." + a_deserialized, a, + "Round-trip failed: deserialized struct did not match original." ); - // Validate serializing error case with an invalid DateTime string - let invalid_date = "invalid_date"; - let bad_b = B { - date: invalid_date.to_string(), - date_optional_none: None, - date_optional_some: Some(invalid_date.to_string()), - date_vector: vec![invalid_date.to_string()], + // Validate deserialization fails for i64::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i64::MIN; + let bad_a = doc! { + "value": invalid_value_for_deserializing, + "value_optional_none": Bson::Null, + "value_optional_some": Some(invalid_value_for_deserializing), + "value_vector": [invalid_value_for_deserializing], }; - let result = serialize_to_document(&bad_b); + let result: Result = deserialize_from_document(bad_a); assert!( result.is_err(), - "Serialization should fail for invalid DateTime strings" + "Deserialization should fail for i64::MIN since it can't be exactly represented as u32" ); let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("BSON error"), - "Expected error message to mention BSON error: {}", + err_string.contains("Cannot convert i64"), + "Expected error message to mention failed i64 to u32 conversion, got: {}", err_string ); } #[test] #[cfg(feature = "serde_with-3")] -fn test_datetime_i64_helper() { +fn test_u64_f64_helper() { let _guard = LOCK.run_concurrently(); #[serde_as] - #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "datetime::FromI64")] - date: i64, + #[serde_as(as = "u64::AsF64")] + pub value: u64, - #[serde_as(as = "Option")] - date_optional_none: Option, + #[serde_as(as = "Option")] + pub value_optional_none: Option, - #[serde_as(as = "Option")] - date_optional_some: Option, + #[serde_as(as = "Option")] + pub value_optional_some: Option, - #[serde_as(as = "Vec")] - date_vector: Vec, + #[serde_as(as = "Vec")] + pub value_vector: Vec, } - let date = DateTime::now(); + let value = 12345; let a = A { - date: date.timestamp_millis(), - date_optional_none: None, - date_optional_some: Some(date.timestamp_millis()), - date_vector: vec![date.timestamp_millis()], + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; // Serialize the struct to BSON @@ -944,30 +1760,30 @@ fn test_datetime_i64_helper() { // Validate serialized data assert_eq!( - doc.get_datetime("date").unwrap(), - &date, - "Expected serialized date to match original date." + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." ); assert_eq!( - doc.get("date_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized date_optional_none to be None." + "Expected serialized value_optional_none to be None." ); assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(date)), - "Expected serialized date_optional_some to match original." + doc.get("value_optional_some"), + Some(&Bson::Double(value as f64)), + "Expected serialized value_optional_some to match original." ); - let date_vector = doc - .get_array("date_vector") - .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + let value_vector = doc + .get_array("value_vector") + .expect("Expected serialized value_vector to be a BSON array."); + let expected_value_vector: Vec = vec![Bson::Double(value as f64)]; assert_eq!( - date_vector, &expected_date_vector, - "Expected each serialized element in date_vector match the original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); // Validate deserialized data @@ -976,38 +1792,55 @@ fn test_datetime_i64_helper() { a_deserialized, a, "Deserialized struct does not match original." ); + + // Validate serializing error case with u64 over size limit + let invalid_value_for_serializing = u64::MAX; + let bad_a: A = A { + value: invalid_value_for_serializing, + value_optional_none: None, + value_optional_some: Some(invalid_value_for_serializing), + value_vector: vec![invalid_value_for_serializing], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Serialization should fail for u64::MAX since it can't be exactly represented as f64" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert u64"), + "Expected error message to mention failed u64 to f64 conversion, got: {}", + err_string + ); } #[test] -#[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] -fn test_datetime_chrono04_datetime_helper() { +#[cfg(feature = "serde_with-3")] +fn test_u64_i32_helper() { let _guard = LOCK.run_concurrently(); - use std::str::FromStr; - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[derive(Deserialize, Serialize, PartialEq, Debug)] struct A { - #[serde_as(as = "datetime::FromChrono04DateTime")] - pub date: chrono::DateTime, + #[serde_as(as = "u64::AsI32")] + value: u64, - #[serde_as(as = "Option")] - pub date_optional_none: Option>, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub date_optional_some: Option>, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub date_vector: Vec>, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let iso = "1996-12-20T00:39:57Z"; - let date: chrono::DateTime = chrono::DateTime::from_str(iso).unwrap(); - let a: A = A { - date, - date_optional_none: None, - date_optional_some: Some(date), - date_vector: vec![date], + let value = 1; + let a = A { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; // Serialize the struct to BSON @@ -1015,69 +1848,107 @@ fn test_datetime_chrono04_datetime_helper() { // Validate serialized data assert_eq!( - doc.get_datetime("date").unwrap().to_chrono(), - date, - "Expected serialized date to match original date." + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." ); assert_eq!( - doc.get("date_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized date_optional_none to be None." + "Expected serialized value_optional_none to be None." ); assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(DateTime::from_chrono(date))), - "Expected serialized date_optional_some to match original." + doc.get("value_optional_some"), + Some(&Bson::Int32(value as i32)), + "Expected serialized value_optional_some to match original." ); - let date_vector = doc - .get_array("date_vector") - .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = vec![Bson::DateTime(date.into())]; + let value_vector = doc + .get_array("value_vector") + .expect("Expected serialized value_vector to be a BSON array."); + let expected_value_vector: Vec = vec![Bson::Int32(value as i32)]; assert_eq!( - date_vector, &expected_date_vector, - "Expected each serialized element in date_vector to be a BSON DateTime matching the \ - original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); // Validate deserialized data let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( a_deserialized, a, - "Deserialized struct does not match original." + "Round-trip failed: deserialized struct did not match original." + ); + + // Validate serialization fails because i32::MAX + 1 is too large to fit in i32 + let invalid_value_for_serializing = i32::MAX as u64 + 1; + let bad_a: A = A { + value: invalid_value_for_serializing, + value_optional_none: None, + value_optional_some: Some(invalid_value_for_serializing), + value_vector: vec![invalid_value_for_serializing], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Serialization should fail for u64::MAX since it can't be exactly represented as i32" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert u64"), + "Expected error message to mention failed u64 to i32 conversion, got: {}", + err_string + ); + + // Validate deserialization fails for i32::MIN because negative values can't be converted to + // u64 + let invalid_value_for_deserializing = i32::MIN; + let bad_a = doc! { + "value": invalid_value_for_deserializing, + "value_optional_none": Bson::Null, + "value_optional_some": Some(invalid_value_for_deserializing), + "value_vector": [invalid_value_for_deserializing], + }; + let result: Result = deserialize_from_document(bad_a); + assert!( + result.is_err(), + "Deserialization should fail for i32::MIN since it can't be exactly represented as u64" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert i32"), + "Expected error message to mention failed i32 to u64 conversion, got: {}", + err_string ); } #[test] -#[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] -fn test_datetime_time03_offset_datetime_helper() { - use time::OffsetDateTime; +#[cfg(feature = "serde_with-3")] +fn test_u64_i64_helper() { let _guard = LOCK.run_concurrently(); - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] + #[derive(Deserialize, Serialize, PartialEq, Debug)] struct A { - #[serde_as(as = "datetime::FromTime03OffsetDateTime")] - pub date: OffsetDateTime, + #[serde_as(as = "u64::AsI64")] + value: u64, - #[serde_as(as = "Option")] - pub date_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub date_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub date_vector: Vec, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let date = DateTime::now(); - let a: A = A { - date: date.to_time_0_3(), - date_optional_none: None, - date_optional_some: Some(date.to_time_0_3()), - date_vector: vec![date.to_time_0_3()], + let value = i64::MAX as u64; + let a = A { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; // Serialize the struct to BSON @@ -1085,38 +1956,79 @@ fn test_datetime_time03_offset_datetime_helper() { // Validate serialized data assert_eq!( - doc.get_datetime("date").unwrap(), - &date, - "Expected serialized date to match original date." + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." ); assert_eq!( - doc.get("date_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized date_optional_none to be None." + "Expected serialized value_optional_none to be None." ); assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(date)), - "Expected serialized date_optional_some to match original." + doc.get("value_optional_some"), + Some(&Bson::Int64(value as i64)), + "Expected serialized value_optional_some to match original." ); - let date_vector = doc - .get_array("date_vector") - .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = vec![Bson::DateTime(date)]; + let value_vector = doc + .get_array("value_vector") + .expect("Expected serialized value_vector to be a BSON array."); + let expected_value_vector: Vec = vec![Bson::Int64(value as i64)]; assert_eq!( - date_vector, &expected_date_vector, - "Expected each serialized element in date_vector to be a BSON DateTime matching the \ - original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); // Validate deserialized data let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( a_deserialized, a, - "Deserialized struct does not match original." + "Round-trip failed: deserialized struct did not match original." + ); + + // Validate serialization fails because i64::MAX + 1 is too large to fit in i64 + let invalid_value_for_serializing = i64::MAX as u64 + 1; + let bad_a: A = A { + value: invalid_value_for_serializing, + value_optional_none: None, + value_optional_some: Some(invalid_value_for_serializing), + value_vector: vec![invalid_value_for_serializing], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Serialization should fail for (i64::MAX as u64) + 1 since it can't be exactly \ + represented as i64" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert u64"), + "Expected error message to mention failed u64 to i64 conversion, got: {}", + err_string + ); + + // Validate deserialization fails for i64::MIN because negative values can't be converted to + // u64 + let invalid_value_for_deserializing = i64::MIN; + let bad_a = doc! { + "value": invalid_value_for_deserializing, + "value_optional_none": Bson::Null, + "value_optional_some": Some(invalid_value_for_deserializing), + "value_vector": [invalid_value_for_deserializing], + }; + let result: Result = deserialize_from_document(bad_a); + assert!( + result.is_err(), + "Deserialization should fail for i64::MIN since it can't be exactly represented as u64" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert i64"), + "Expected error message to mention failed i64 to u64 conversion, got: {}", + err_string ); } @@ -1374,47 +2286,6 @@ fn test_uuid_1_helpers() { assert_eq!(a.uuid, uuid); } -#[test] -fn test_timestamp_helpers() { - let _guard = LOCK.run_concurrently(); - - #[derive(Deserialize, Serialize)] - struct A { - #[serde(with = "u32_as_timestamp")] - pub time: u32, - } - - let time = 12345; - let a = A { time }; - let doc = serialize_to_document(&a).unwrap(); - let timestamp = doc.get_timestamp("time").unwrap(); - assert_eq!(timestamp.time, time); - assert_eq!(timestamp.increment, 0); - let a: A = deserialize_from_document(doc).unwrap(); - assert_eq!(a.time, time); - - #[derive(Deserialize, Serialize)] - struct B { - #[serde(with = "timestamp_as_u32")] - pub timestamp: Timestamp, - } - - let time = 12345; - let timestamp = Timestamp { time, increment: 0 }; - let b = B { timestamp }; - let val = serde_json::to_value(b).unwrap(); - assert_eq!(val["timestamp"], time); - let b: B = serde_json::from_value(val).unwrap(); - assert_eq!(b.timestamp, timestamp); - - let timestamp = Timestamp { - time: 12334, - increment: 1, - }; - let b = B { timestamp }; - assert!(serde_json::to_value(b).is_err()); -} - #[test] fn large_dates() { let _guard = LOCK.run_concurrently();