From a54433eb60fd9296c57c25664d70ecbe0ee1c5d9 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 8 Jul 2025 11:07:13 -0400 Subject: [PATCH 01/61] Refactor BSON DateTime serde converters using serde_conv_doc macro --- src/serde_helpers.rs | 219 ++++++++++++++++++------------------------- 1 file changed, 89 insertions(+), 130 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index dbc93e0a..8ddd1ae0 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -278,147 +278,106 @@ pub mod time_0_3_offsetdatetime_as_bson_datetime { #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod bson_datetime { - use crate::{Bson, DateTime}; + use crate::{macros::serde_conv_doc, DateTime}; use chrono::Utc; - use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; use std::result::Result; - /// Contains functions to serialize a [`crate::DateTime`] as an RFC 3339 (ISO 8601) formatted - /// string and deserialize a [`crate::DateTime`] from an RFC 3339 (ISO 8601) formatted - /// string. - /// - /// ```rust - /// # #[cfg(feature = "serde_with-3")] - /// { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Event { - /// #[serde_as(as = "bson_datetime::AsRfc3339String")] - /// pub date: bson::DateTime, - /// } - /// # } - /// ``` - pub struct AsRfc3339String; - - impl SerializeAs for AsRfc3339String { - fn serialize_as(val: &crate::DateTime, serializer: S) -> Result - where - S: Serializer, - { - let formatted = val.try_to_rfc3339_string().map_err(|e| { - ser::Error::custom(format!("cannot format {} as RFC 3339: {}", val, e)) - })?; - serializer.serialize_str(&formatted) - } - } - - impl<'de> DeserializeAs<'de, crate::DateTime> for AsRfc3339String { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let iso = String::deserialize(deserializer)?; - let date = crate::DateTime::parse_rfc3339_str(&iso).map_err(|e| { - de::Error::custom(format!( - "cannot parse RFC 3339 datetime from \"{}\": {}", - iso, e - )) - })?; - Ok(date) - } - } - - /// 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`]. - /// - /// ```rust - /// # #[cfg(feature = "serde_with-3")] - /// { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Event { - /// #[serde_as(as = "bson_datetime::FromRfc3339String")] - /// pub date: String, - /// } - /// # } - /// ``` - pub struct FromRfc3339String; - - impl SerializeAs for FromRfc3339String { - fn serialize_as(val: &String, serializer: S) -> Result - where - S: Serializer, - { - let date = DateTime::parse_rfc3339_str(val).map_err(|e| { - ser::Error::custom(format!("cannot convert {} to DateTime: {}", val, e)) - })?; - Bson::DateTime(date).serialize(serializer) - } - } - - impl<'de> DeserializeAs<'de, String> for FromRfc3339String { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let date = DateTime::deserialize(deserializer)?; + serde_conv_doc!( + /// Contains functions to serialize a [`crate::DateTime`] as an RFC 3339 (ISO 8601) formatted + /// string and deserialize a [`crate::DateTime`] from an RFC 3339 (ISO 8601) formatted + /// string. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::bson_datetime; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde_as(as = "bson_datetime::AsRfc3339String")] + /// pub date: bson::DateTime, + /// } + /// # } + /// ``` + pub AsRfc3339String, + DateTime, + |date: &DateTime| -> Result { date.try_to_rfc3339_string().map_err(|e| { - de::Error::custom(format!("cannot format {} as RFC 3339: {}", date, e)) + format!("Cannot format DateTime {} as String: {}", date, e) }) + }, + |string: String| -> Result { + DateTime::parse_rfc3339_str(&string).map_err(|e| format!("Cannot format String {} as DateTime: {}", string, e)) } - } + ); - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))) - )] - /// Contains functions to serialize a [`chrono::DateTime`] as a [`crate::DateTime`] and - /// deserialize a [`chrono::DateTime`] from a [`crate::DateTime`]. - /// - /// ```rust - /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Event { - /// #[serde_as(as = "bson_datetime::FromChronoDateTime")] - /// pub date: chrono::DateTime, - /// } - /// # } - /// ``` - pub struct FromChronoDateTime; - - impl SerializeAs> for FromChronoDateTime { - fn serialize_as(val: &chrono::DateTime, serializer: S) -> Result - where - S: Serializer, - { - let datetime = DateTime::from_chrono(val.to_owned()); - datetime.serialize(serializer) + serde_conv_doc!( + /// 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`]. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::bson_datetime; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde_as(as = "bson_datetime::FromRfc3339String")] + /// pub date: String, + /// } + /// # } + /// ``` + pub FromRfc3339String, + String, + |string: &String| -> Result { + DateTime::parse_rfc3339_str(string).map_err(|e| format!("Cannot format String {} as DateTime: {}", string, e)) + }, + |date: DateTime| -> Result { + date.try_to_rfc3339_string().map_err(|e| { + format!("Cannot format DateTime {} as String: {}", date, e) + }) } - } + ); - impl<'de> DeserializeAs<'de, chrono::DateTime> for FromChronoDateTime { - fn deserialize_as(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - let datetime = DateTime::deserialize(deserializer)?; - Ok(datetime.to_chrono()) + serde_conv_doc!( + #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))) + )] + /// Contains functions to serialize a [`chrono::DateTime`] as a [`crate::DateTime`] and + /// deserialize a [`chrono::DateTime`] from a [`crate::DateTime`]. + /// + /// ```rust + /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::bson_datetime; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde_as(as = "bson_datetime::FromChronoDateTime")] + /// pub date: chrono::DateTime, + /// } + /// # } + /// ``` + pub FromChronoDateTime, + chrono::DateTime, + |chrono_date: &chrono::DateTime| -> Result { + Ok(DateTime::from_chrono(chrono_date.to_owned())) + }, + |bson_date: DateTime| -> Result, String> { + Ok(bson_date.to_chrono()) } - } + ); } /// Contains functions to `serialize` a `i64` integer as [`crate::DateTime`] and From a51f29e85b22ac89a65d4b3f181f707df95d2125 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 8 Jul 2025 16:11:07 -0400 Subject: [PATCH 02/61] Refactor OffsetDateTime and i64 to DateTime helpers to use serde_conv --- src/serde_helpers.rs | 144 +++++++++------------- src/tests/serde.rs | 275 +++++++++++++++++++++++++++++++------------ 2 files changed, 258 insertions(+), 161 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 8ddd1ae0..45f7f296 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -9,17 +9,6 @@ use std::{ use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; -#[doc(inline)] -pub use i64_as_bson_datetime::{ - deserialize as deserialize_i64_from_bson_datetime, - serialize as serialize_i64_as_bson_datetime, -}; -#[cfg(feature = "time-0_3")] -#[doc(inline)] -pub use time_0_3_offsetdatetime_as_bson_datetime::{ - deserialize as deserialize_time_0_3_offsetdatetime_from_bson_datetime, - serialize as serialize_time_0_3_offsetdatetime_as_bson_datetime, -}; #[doc(inline)] pub use timestamp_as_u32::{ deserialize as deserialize_timestamp_from_u32, @@ -232,49 +221,6 @@ pub mod u64_as_f64 { } } -/// Contains functions to serialize a [`time::OffsetDateTime`] as a [`crate::DateTime`] and -/// deserialize a [`time::OffsetDateTime`] from a [`crate::DateTime`]. -/// -/// ```rust -/// # #[cfg(feature = "time-0_3")] -/// # { -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::time_0_3_offsetdatetime_as_bson_datetime; -/// #[derive(Serialize, Deserialize)] -/// struct Event { -/// #[serde(with = "time_0_3_offsetdatetime_as_bson_datetime")] -/// pub date: time::OffsetDateTime, -/// } -/// # } -/// ``` -#[cfg(feature = "time-0_3")] -#[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] -pub mod time_0_3_offsetdatetime_as_bson_datetime { - use crate::DateTime; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use std::result::Result; - - /// Deserializes a [`time::OffsetDateTime`] from a [`crate::DateTime`]. - #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let datetime = DateTime::deserialize(deserializer)?; - Ok(datetime.to_time_0_3()) - } - - /// Serializes a [`time::OffsetDateTime`] as a [`crate::DateTime`]. - #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] - pub fn serialize( - val: &time::OffsetDateTime, - serializer: S, - ) -> Result { - let datetime = DateTime::from_time_0_3(val.to_owned()); - datetime.serialize(serializer) - } -} - #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod bson_datetime { @@ -372,46 +318,70 @@ pub mod bson_datetime { pub FromChronoDateTime, chrono::DateTime, |chrono_date: &chrono::DateTime| -> Result { - Ok(DateTime::from_chrono(chrono_date.to_owned())) + Ok(DateTime::from_chrono(*chrono_date)) }, |bson_date: DateTime| -> Result, String> { Ok(bson_date.to_chrono()) } ); -} - -/// Contains functions to `serialize` a `i64` integer as [`crate::DateTime`] and -/// `deserialize` a `i64` integer from [`crate::DateTime`]. -/// -/// ### The i64 should represent seconds `(DateTime::timestamp_millis(..))`. -/// -/// ```rust -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::i64_as_bson_datetime; -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "i64_as_bson_datetime")] -/// pub now: i64, -/// } -/// ``` -pub mod i64_as_bson_datetime { - use crate::DateTime; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - /// Deserializes a i64 integer from a DateTime. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let date: DateTime = DateTime::deserialize(deserializer)?; - Ok(date.timestamp_millis()) - } + serde_conv_doc!( + /// Contains functions to `serialize` a `i64` integer as [`crate::DateTime`] and + /// `deserialize` a `i64` integer from [`crate::DateTime`]. + /// + /// ### The i64 should represent seconds `(DateTime::timestamp_millis(..))`. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::bson_datetime; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde(with = "bson_datetime::FromI64")] + /// pub now: i64, + /// } + /// # } + /// ``` + pub FromI64, + i64, + |val: &i64| -> Result { + Ok(DateTime::from_millis(*val)) + }, + |date: DateTime| -> Result { + Ok(date.timestamp_millis()) + } + ); - /// Serializes a i64 integer as a DateTime. - pub fn serialize(val: &i64, serializer: S) -> Result { - let date_time = DateTime::from_millis(*val); - date_time.serialize(serializer) - } + serde_conv_doc!( + /// Contains functions to serialize a [`time::OffsetDateTime`] as a [`crate::DateTime`] and + /// deserialize a [`time::OffsetDateTime`] from a [`crate::DateTime`]. + /// + /// ```rust + /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::bson_datetime; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde(with = "bson_datetime::FromTime03OffsetDatetime")] + /// pub date: time::OffsetDateTime, + /// } + /// # } + /// ``` + pub FromTime03OffsetDateTime, + time::OffsetDateTime, + |val: &time::OffsetDateTime| -> Result { + Ok(DateTime::from_time_0_3(*val)) + }, + |date: DateTime| -> Result { + Ok(date.to_time_0_3()) + } + ); } #[allow(unused_macros)] diff --git a/src/tests/serde.rs b/src/tests/serde.rs index b25456de..04510c6d 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -7,14 +7,7 @@ use crate::{ deserialize_from_document, doc, oid::ObjectId, - serde_helpers::{ - self, - bson_datetime, - i64_as_bson_datetime, - object_id, - timestamp_as_u32, - u32_as_timestamp, - }, + serde_helpers::{self, bson_datetime, object_id, timestamp_as_u32, u32_as_timestamp}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -31,6 +24,7 @@ use crate::{ use serde::{Deserialize, Serialize}; use serde_json::json; use serde_with::serde_as; +use time::OffsetDateTime; use std::{ collections::BTreeMap, @@ -749,8 +743,6 @@ fn test_unsigned_helpers() { #[test] fn test_datetime_helpers() { - use time::{format_description::well_known::Rfc3339, OffsetDateTime}; - let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] @@ -865,34 +857,6 @@ fn test_datetime_helpers() { ); } - #[cfg(feature = "time-0_3")] - { - use time::macros::datetime; - - #[derive(Deserialize, Serialize)] - struct B { - #[serde(with = "serde_helpers::time_0_3_offsetdatetime_as_bson_datetime")] - pub date: time::OffsetDateTime, - } - - 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 = datetime!(2020-06-09 10:58:07.095 UTC); - assert_eq!(b.date, expected); - let doc = serialize_to_document(&b).unwrap(); - assert_eq!(doc.get_datetime("date").unwrap().to_time_0_3(), expected); - let b: B = deserialize_from_document(doc).unwrap(); - assert_eq!(b.date, expected); - } - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] { use std::str::FromStr; @@ -1008,20 +972,21 @@ fn test_datetime_helpers() { pub date_vector: Vec, } - let date = "2020-06-09T10:58:07.095Z"; + let date = DateTime::now(); let c = C { - date: date.to_string(), + date: date.try_to_rfc3339_string().unwrap(), date_optional_none: None, - date_optional_some: Some(date.to_string()), - date_vector: vec![date.to_string()], + 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(&c).unwrap(); // Validate serialized data - assert!( - doc.get_datetime("date").is_ok(), + assert_eq!( + *doc.get_datetime("date").unwrap(), + date, "Expected serialized date to be a BSON DateTime." ); @@ -1031,19 +996,14 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - let expected_date = DateTime::from_time_0_3( - OffsetDateTime::parse(date, &Rfc3339) - .expect("Failed to parse date string into DateTime."), - ); - match doc.get("date_optional_some") { Some(Bson::DateTime(value)) => { assert_eq!( - *value, expected_date, + *value, date, "Expected serialized date_optional_some to match original." ) } - _ => panic!("Expected serialized date_optional_some to be a BSON String."), + _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), } let date_vector = doc @@ -1065,7 +1025,7 @@ fn test_datetime_helpers() { // Validate deserialized data assert_eq!( c_deserialized.date, - date.to_string(), + date.try_to_rfc3339_string().unwrap(), "Expected deserialized date to match original." ); @@ -1076,13 +1036,13 @@ fn test_datetime_helpers() { assert_eq!( c_deserialized.date_optional_some, - Some(date.to_string()), + Some(date.try_to_rfc3339_string().unwrap()), "Expected deserialized date_optional_some to match original." ); assert_eq!( c_deserialized.date_vector, - vec![date.to_string()], + vec![date.try_to_rfc3339_string().unwrap()], "Expected deserialized date_vector to match original." ); @@ -1106,6 +1066,193 @@ fn test_datetime_helpers() { err_string ); } + + #[cfg(feature = "serde_with-3")] + { + #[serde_as] + #[derive(Serialize, Deserialize)] + struct A { + #[serde_as(as = "bson_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." + ); + + match doc.get("date_optional_some") { + Some(Bson::DateTime(value)) => { + assert_eq!( + *value, date, + "Expected serialized date_optional_some to match original." + ) + } + _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), + } + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = a + .date_vector + .into_iter() + .map(|dt| Bson::DateTime(DateTime::from_millis(dt))) + .collect(); + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector match the original." + ); + + // Deserialize the BSON back to the struct + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + a_deserialized.date, + date.timestamp_millis(), + "Expected deserialized date to match original." + ); + + assert_eq!( + a_deserialized.date_optional_none, None, + "Expected deserialized date_optional_none to be None." + ); + + assert_eq!( + a_deserialized.date_optional_some, + Some(date.timestamp_millis()), + "Expected deserialized date_optional_some to match original." + ); + + assert_eq!( + a_deserialized.date_vector, + vec![date.timestamp_millis()], + "Expected deserialized date_vector to match original." + ); + } + + #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] + { + #[serde_as] + #[derive(Deserialize, Serialize)] + struct A { + #[serde_as(as = "bson_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 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(); + + // 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." + ); + + match doc.get("date_optional_some") { + Some(Bson::DateTime(value)) => { + assert_eq!( + *value, date, + "Expected serialized date_optional_some to match original." + ) + } + _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), + } + + let date_vector = doc + .get_array("date_vector") + .expect("Expected serialized date_vector to be a BSON array."); + let expected_date_vector: Vec = a + .date_vector + .into_iter() + .map(|dt| Bson::DateTime(dt.into())) + .collect(); + assert_eq!( + date_vector, &expected_date_vector, + "Expected each serialized element in date_vector to be a BSON DateTime matching the \ + original." + ); + + // Deserialize the BSON back to the struct + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + a_deserialized.date, + date.to_time_0_3(), + "Expected deserialized date to match original." + ); + + assert_eq!( + a_deserialized.date_optional_none, None, + "Expected deserialized date_optional_none to be None." + ); + + assert_eq!( + a_deserialized.date_optional_some, + Some(date.to_time_0_3()), + "Expected deserialized date_optional_some to match the original." + ); + + assert_eq!( + a_deserialized.date_vector, + vec![date.to_time_0_3()], + "Expected deserialized date_vector to match original." + ); + } } #[test] @@ -1334,26 +1481,6 @@ fn test_oid_helpers() { } } -#[test] -fn test_i64_as_bson_datetime() { - let _guard = LOCK.run_concurrently(); - - #[derive(Serialize, Deserialize)] - struct A { - #[serde(with = "i64_as_bson_datetime")] - now: i64, - } - - let now = DateTime::now(); - let a = A { - now: now.timestamp_millis(), - }; - let doc = serialize_to_document(&a).unwrap(); - assert_eq!(doc.get_datetime("now").unwrap(), &now); - let a: A = deserialize_from_document(doc).unwrap(); - assert_eq!(a.now, now.timestamp_millis()); -} - #[test] #[cfg(feature = "uuid-1")] fn test_uuid_1_helpers() { From 03e9a518b46ab4a44da99e841f8c3cf8fbb4e802 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Wed, 9 Jul 2025 13:20:48 -0400 Subject: [PATCH 03/61] Refactor type converters betweem timestamp and u32 with serde_conv --- src/serde_helpers.rs | 134 +++++++++++++--------- src/tests/serde.rs | 266 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 311 insertions(+), 89 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 45f7f296..1f15c002 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -9,11 +9,6 @@ use std::{ use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; -#[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)] @@ -89,7 +84,7 @@ pub mod object_id { /// ObjectId from a hex string /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { + /// # { /// # use serde::{Serialize, Deserialize}; /// # use bson::serde_helpers::object_id; /// # use serde_with::serde_as; @@ -117,7 +112,7 @@ pub mod object_id { /// hex string from an ObjectId /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { + /// # { /// # use serde::{Serialize, Deserialize}; /// # use bson::serde_helpers::object_id; /// # use serde_with::serde_as; @@ -237,7 +232,7 @@ pub mod bson_datetime { /// /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { + /// # { /// # use serde::{Serialize, Deserialize}; /// # use bson::serde_helpers::bson_datetime; /// # use serde_with::serde_as; @@ -268,7 +263,7 @@ pub mod bson_datetime { /// /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { + /// # { /// # use serde::{Serialize, Deserialize}; /// # use bson::serde_helpers::bson_datetime; /// # use serde_with::serde_as; @@ -326,21 +321,21 @@ pub mod bson_datetime { ); serde_conv_doc!( - /// Contains functions to `serialize` a `i64` integer as [`crate::DateTime`] and - /// `deserialize` a `i64` integer from [`crate::DateTime`]. + /// Contains functions to serialize a `i64` integer as [`crate::DateTime`] and + /// deserialize a `i64` integer from [`crate::DateTime`]. /// /// ### The i64 should represent seconds `(DateTime::timestamp_millis(..))`. /// /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { + /// # { /// # use serde::{Serialize, Deserialize}; /// # use bson::serde_helpers::bson_datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { - /// #[serde(with = "bson_datetime::FromI64")] + /// #[serde_as(as = "bson_datetime::FromI64")] /// pub now: i64, /// } /// # } @@ -368,7 +363,7 @@ pub mod bson_datetime { /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde(with = "bson_datetime::FromTime03OffsetDatetime")] + /// #[serde_as(as = "bson_datetime::FromTime03OffsetDatetime")] /// pub date: time::OffsetDateTime, /// } /// # } @@ -384,6 +379,78 @@ pub mod bson_datetime { ); } +#[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}; + use std::result::Result; + + serde_conv_doc!( + /// 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 + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::{serde_helpers::u32, Timestamp}; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "u32::FromTimestamp")] + /// pub timestamp: Timestamp, + /// } + /// # } + /// ``` + pub FromTimestamp, + Timestamp, + |ts: &Timestamp| -> Result { + if ts.increment != 0 { + return Err(format!("Cannot format Timestamp with a non-zero increment to u32: {:?}", ts)); + } + Ok(ts.time) + }, + |val: u32| -> Result { + Ok(Timestamp { time: val, increment: 0 }) + } + ); + + serde_conv_doc!( + /// 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 + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::u32; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde_as(as = "u32_as_timestamp")] + /// pub time: u32, + /// } + /// # } + /// ``` + pub AsTimestamp, + u32, + |val: &u32| -> Result { + Ok(Timestamp { time: *val, increment: 0 }) + }, + |ts: Timestamp| -> Result { + if ts.increment != 0 { + return Err(format!("Cannot format Timestamp with a non-zero increment to u32: {:?}", ts)); + } + Ok(ts.time) + } + ); +} + #[allow(unused_macros)] macro_rules! as_binary_mod { ($feat:meta, $uu:path) => { @@ -582,45 +649,6 @@ pub mod u32_as_timestamp { } } -/// 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 04510c6d..e4ab8d8f 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, bson_datetime, object_id, timestamp_as_u32, u32_as_timestamp}, + serde_helpers::{self, bson_datetime, object_id, u32}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -1057,7 +1057,7 @@ fn test_datetime_helpers() { let result = serialize_to_document(&bad_c); assert!( result.is_err(), - "Deserialization should fail for invalid DateTime strings" + "Serialization should fail for invalid DateTime strings" ); let err_string = format!("{:?}", result.unwrap_err()); assert!( @@ -1359,7 +1359,7 @@ fn test_oid_helpers() { let result = serialize_to_document(&bad_a); assert!( result.is_err(), - "Deserialization should fail for invalid ObjectId strings" + "Serialization should fail for invalid ObjectId strings" ); let err_string = format!("{:?}", result.unwrap_err()); assert!( @@ -1510,44 +1510,238 @@ fn test_uuid_1_helpers() { } #[test] -fn test_timestamp_helpers() { +fn test_u32_helpers() { let _guard = LOCK.run_concurrently(); - #[derive(Deserialize, Serialize)] - struct A { - #[serde(with = "u32_as_timestamp")] - pub time: u32, - } + #[cfg(feature = "serde_with-3")] + { + #[serde_as] + #[derive(Deserialize, Serialize)] + struct B { + #[serde_as(as = "u32::FromTimestamp")] + pub timestamp: Timestamp, - 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); + #[serde_as(as = "Option")] + pub timestamp_optional_none: Option, - #[derive(Deserialize, Serialize)] - struct B { - #[serde(with = "timestamp_as_u32")] - pub timestamp: Timestamp, - } + #[serde_as(as = "Option")] + pub timestamp_optional_some: Option, - 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()); + #[serde_as(as = "Vec")] + pub timestamp_vector: Vec, + } + + let time = 12345; + let timestamp = Timestamp { time, increment: 0 }; + let b = B { + timestamp, + timestamp_optional_none: None, + timestamp_optional_some: Some(timestamp), + timestamp_vector: vec![timestamp], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).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 = b + .timestamp_vector + .iter() + .map(|ts| Bson::Int64(ts.time as i64)) + .collect(); + assert_eq!( + timestamp_vector, &expected_timestamp_vector, + "Expected each serialized element in timestamp_vector to match the original." + ); + + // Deserialize the BSON back to the struct + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + b_deserialized.timestamp, timestamp, + "Expected deserialized timestamp to match the original." + ); + + assert_eq!( + b_deserialized.timestamp_optional_none, None, + "Expected deserialized timestamp_optional_none to be None." + ); + + assert_eq!( + b_deserialized.timestamp_optional_some, + Some(timestamp), + "Expected deserialized timestamp_optional_some to match the original." + ); + + assert_eq!( + b_deserialized.timestamp_vector, + vec![timestamp], + "Expected deserialized timestamp_vector to match the original." + ); + + // Validate serializing error case with an invalid Timestamp + let invalid_timestamp_for_serializing = Timestamp { + time: 0, + increment: 2, + }; + let bad_b: B = B { + 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_b); + assert!( + result.is_err(), + "Serialization should fail for Timestamp with increment != 0" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot format Timestamp with a non-zero increment to u32"), + "Expected error message to mention non-zero increment: {}", + err_string + ); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug)] + struct A { + #[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 a = A { + time, + time_optional_none: None, + time_optional_some: Some(time), + time_vector: vec![time], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // 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." + ); + + match doc.get("time_optional_some") { + Some(Bson::Timestamp(ts)) => { + assert_eq!( + *ts, + Timestamp { time, increment: 0 }, + "Expected serialized time_optional_some to match original time." + ) + } + _ => panic!("Expected serialized time_optional_some to be a BSON Timestamp."), + } + + let time_vector = doc + .get_array("time_vector") + .expect("Expected serialized time_vector to be a BSON array."); + let expected_time_vector: Vec = a + .time_vector + .iter() + .map(|val| { + Bson::Timestamp(Timestamp { + time: *val, + increment: 0, + }) + }) + .collect(); + assert_eq!( + time_vector, &expected_time_vector, + "Expected each serialized element in time_vector to match the original." + ); + + // Deserialize the BSON back to the struct + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + a_deserialized.time, time, + "Expected deserialized time to match the original." + ); + + assert_eq!( + a_deserialized.time_optional_none, None, + "Expected deserialized time_optional_none to be None." + ); + + assert_eq!( + a_deserialized.time_optional_some, + Some(time), + "Expected deserialized time_optional_some to match the original." + ); + + assert_eq!( + a_deserialized.time_vector, + vec![time], + "Expected deserialized time_vector to match the original." + ); + + // Validate deserializing error case with an invalid Timestamp + let invalid_timestamp_for_deserializing = Timestamp { + time: 0, + increment: 2, + }; + 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 format Timestamp with a non-zero increment to u32"), + "Expected error message to mention non-zero increment: {}", + err_string + ); + } } #[test] From f1577fd64f792064af34b51b4de80501cc71f12a Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Wed, 9 Jul 2025 16:26:06 -0400 Subject: [PATCH 04/61] Refactor u32 and u64 to f64 converters to use serde_conv --- src/serde_helpers.rs | 175 ++++++++++++++++---------------- src/tests/serde.rs | 230 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 280 insertions(+), 125 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 1f15c002..11b5cb5e 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -9,16 +9,11 @@ use std::{ use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; -#[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::{ @@ -135,87 +130,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 - ))) - } - } -} - #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod bson_datetime { @@ -410,7 +324,7 @@ pub mod u32 { Timestamp, |ts: &Timestamp| -> Result { if ts.increment != 0 { - return Err(format!("Cannot format Timestamp with a non-zero increment to u32: {:?}", ts)); + return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", ts)); } Ok(ts.time) }, @@ -419,6 +333,7 @@ pub mod u32 { } ); + // TODO: fix the import, change to mod timestamp serde_conv_doc!( /// 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. @@ -444,13 +359,96 @@ pub mod u32 { }, |ts: Timestamp| -> Result { if ts.increment != 0 { - return Err(format!("Cannot format Timestamp with a non-zero increment to u32: {:?}", ts)); + return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", ts)); } Ok(ts.time) } ); } +// TODO: change u32 -> timestamp (reverse all the text) + +#[cfg(feature = "serde_with-3")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] +pub mod f64 { + use crate::macros::serde_conv_doc; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + use std::result::Result; + + serde_conv_doc!( + /// Contains functions to serialize a `u64` as an `f64` (BSON double) and deserialize a + /// `u64` from an `f64` (BSON double). + /// + /// Deserialization errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::f64; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct FileInfo { + /// #[serde_as(as = "f64::FromU64")] + /// pub size_bytes: u64, + /// } + /// # } + /// ``` + pub FromU64, + u64, + |val: &u64| -> Result { + if val < &u64::MAX && *val == *val as f64 as u64 { + Ok(*val as f64) + } else { + Err(format!("Cannot convert u64 {} to f64 (BSON double)", val)) + } + }, + |val: f64| -> Result { + if (val - val as u64 as f64).abs() <= f64::EPSILON { + Ok(val as u64) + } else { + Err(format!("Cannot convert f64 (BSON double) {} to u64", val)) + } + } + ); + + serde_conv_doc!( + /// Contains functions to serialize a `u32` as an `f64` (BSON double) and deserialize a + /// `u32` from an `f64` (BSON double). + /// + /// Deserialization errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::f64; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct FileInfo { + /// #[serde_as(as = "f64::fromU32")] + /// pub size_bytes: u32, + /// } + /// # } + /// ``` + pub FromU32, + u32, + |val: &u32| -> Result { + Ok(*val as f64) + }, + |val: f64| -> Result { + if (val - val as u32 as f64).abs() <= f64::EPSILON { + Ok(val as u32) + } else { + Err(format!("Cannot convert f64 (BSON double) {} to u32", val)) + } + } + ); +} + #[allow(unused_macros)] macro_rules! as_binary_mod { ($feat:meta, $uu:path) => { @@ -613,6 +611,7 @@ pub mod uuid_1_as_c_sharp_legacy_binary { ); } +// TODO: delete this! /// 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. /// diff --git a/src/tests/serde.rs b/src/tests/serde.rs index e4ab8d8f..6b4ae16b 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, bson_datetime, object_id, u32}, + serde_helpers::{self, bson_datetime, f64, object_id, u32}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -705,40 +705,6 @@ fn test_unsigned_helpers() { }; let doc_result = serialize_to_document(&b); assert!(doc_result.is_err()); - - #[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, - } - - let f = F { - num_1: 101, - num_2: 12345, - }; - 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); - - 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()); - - let f = F { - num_1: 1, - num_2: u64::MAX - 255, - }; - let doc_result = serialize_to_document(&f); - assert!(doc_result.is_err()); } #[test] @@ -1619,7 +1585,7 @@ fn test_u32_helpers() { ); let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("Cannot format Timestamp with a non-zero increment to u32"), + err_string.contains("Cannot convert Timestamp with a non-zero increment to u32"), "Expected error message to mention non-zero increment: {}", err_string ); @@ -1737,13 +1703,203 @@ fn test_u32_helpers() { ); let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("Cannot format Timestamp with a non-zero increment to u32"), + err_string.contains("Cannot convert Timestamp with a non-zero increment to u32"), "Expected error message to mention non-zero increment: {}", err_string ); } } +#[test] +fn test_f64_helpers() { + #[cfg(feature = "serde_with-3")] + { + #[serde_as] + #[derive(Deserialize, Serialize, Debug)] + struct A { + #[serde_as(as = "f64::FromU64")] + pub value: u64, + + #[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 value = 12345; + 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(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); + + 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 time." + ); + + 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." + ); + + // Deserialize the BSON back to the struct + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + a_deserialized.value, value, + "Expected deserialized value to match the original." + ); + + assert_eq!( + a_deserialized.value_optional_none, None, + "Expected deserialized val_optional_none to be None." + ); + + assert_eq!( + a_deserialized.value_optional_some, + Some(value), + "Expected deserialized val_optional_some to match the original." + ); + + assert_eq!( + a_deserialized.value_vector, + vec![value], + "Expected deserialized val_vector to match the 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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug)] + struct B { + #[serde_as(as = "f64::FromU32")] + 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 value = 12345; + let b = B { + 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(); + + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); + + 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 time." + ); + + 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." + ); + + // Deserialize the BSON back to the struct + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + b_deserialized.value, value, + "Expected deserialized value to match the original." + ); + + assert_eq!( + b_deserialized.value_optional_none, None, + "Expected deserialized val_optional_none to be None." + ); + + assert_eq!( + b_deserialized.value_optional_some, + Some(value), + "Expected deserialized val_optional_some to match the original." + ); + + assert_eq!( + b_deserialized.value_vector, + vec![value], + "Expected deserialized val_vector to match the original." + ); + } +} + #[test] fn large_dates() { let _guard = LOCK.run_concurrently(); From b8ad90438cc7807072931d32c9efcce1c4ecee78 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Wed, 9 Jul 2025 16:32:10 -0400 Subject: [PATCH 05/61] Rename u32 module to timestamp --- src/serde_helpers.rs | 17 +++++++---------- src/tests/serde.rs | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 11b5cb5e..93066767 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -295,7 +295,7 @@ pub mod bson_datetime { #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod u32 { +pub mod timestamp { use crate::{macros::serde_conv_doc, Timestamp}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; @@ -310,17 +310,17 @@ pub mod u32 { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32, Timestamp}; + /// # use bson::{serde_helpers::timestamp, Timestamp}; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { - /// #[serde_as(as = "u32::FromTimestamp")] + /// #[serde_as(as = "timestamp::AsU32")] /// pub timestamp: Timestamp, /// } /// # } /// ``` - pub FromTimestamp, + pub AsU32, Timestamp, |ts: &Timestamp| -> Result { if ts.increment != 0 { @@ -333,7 +333,6 @@ pub mod u32 { } ); - // TODO: fix the import, change to mod timestamp serde_conv_doc!( /// 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. @@ -342,17 +341,17 @@ pub mod u32 { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u32; + /// # use bson::serde_helpers::timestamp; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "u32_as_timestamp")] + /// #[serde_as(as = "timestamp::FromU32")] /// pub time: u32, /// } /// # } /// ``` - pub AsTimestamp, + pub FromU32, u32, |val: &u32| -> Result { Ok(Timestamp { time: *val, increment: 0 }) @@ -366,8 +365,6 @@ pub mod u32 { ); } -// TODO: change u32 -> timestamp (reverse all the text) - #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod f64 { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 6b4ae16b..fb4a6185 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, bson_datetime, f64, object_id, u32}, + serde_helpers::{self, bson_datetime, f64, object_id, timestamp}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -1476,7 +1476,7 @@ fn test_uuid_1_helpers() { } #[test] -fn test_u32_helpers() { +fn test_timestamp_helpers() { let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] @@ -1484,16 +1484,16 @@ fn test_u32_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct B { - #[serde_as(as = "u32::FromTimestamp")] + #[serde_as(as = "timestamp::AsU32")] pub timestamp: Timestamp, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub timestamp_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub timestamp_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub timestamp_vector: Vec, } @@ -1593,16 +1593,16 @@ fn test_u32_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug)] struct A { - #[serde_as(as = "u32::AsTimestamp")] + #[serde_as(as = "timestamp::FromU32")] pub time: u32, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub time_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub time_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub time_vector: Vec, } @@ -1764,6 +1764,7 @@ fn test_f64_helpers() { .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)]; + // TODO: check whether this can be applied to other converters assert_eq!( value_vector, &expected_value_vector, From a1ad2de667a6feb946e891c2e647b4c6df338f6c Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Wed, 9 Jul 2025 16:33:43 -0400 Subject: [PATCH 06/61] Clean up remaining u32_as_timestamp converter --- src/serde_helpers.rs | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 93066767..2b22f610 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -9,11 +9,6 @@ use std::{ use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; -#[doc(inline)] -pub use u32_as_timestamp::{ - deserialize as deserialize_u32_from_timestamp, - serialize as serialize_u32_as_timestamp, -}; #[cfg(feature = "uuid-1")] #[doc(inline)] pub use uuid_1_as_binary::{ @@ -608,43 +603,6 @@ pub mod uuid_1_as_c_sharp_legacy_binary { ); } -// TODO: delete this! -/// 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) - } -} - /// 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. From 5529ec81c0cf0c90438e53d0ab4a4255c96ef71a Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Thu, 10 Jul 2025 09:36:32 -0400 Subject: [PATCH 07/61] Fix struct capitalization --- src/serde_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 2b22f610..aaa3a9c8 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -272,7 +272,7 @@ pub mod bson_datetime { /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromTime03OffsetDatetime")] + /// #[serde_as(as = "bson_datetime::FromTime03OffsetDatetTime")] /// pub date: time::OffsetDateTime, /// } /// # } From a7c182a320697480c8ee7580b417770ba05d75d8 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Thu, 10 Jul 2025 13:49:28 -0400 Subject: [PATCH 08/61] Refactor unsigned (u32, u64) to signed (i32, i64) converters using serde_conv --- src/extjson/models.rs | 7 +- src/serde_helpers.rs | 163 +++++++++++++--- src/tests/serde.rs | 439 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 529 insertions(+), 80 deletions(-) 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 aaa3a9c8..1cd06051 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,13 +1,12 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde use std::{ - convert::TryFrom, marker::PhantomData, ops::{Deref, DerefMut}, result::Result, }; -use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; #[cfg(feature = "uuid-1")] #[doc(inline)] @@ -34,36 +33,8 @@ 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")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { use crate::{macros::serde_conv_doc, oid::ObjectId}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -441,6 +412,136 @@ pub mod f64 { ); } +#[cfg(feature = "serde_with-3")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] +pub mod u32 { + use crate::macros::serde_conv_doc; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + use std::result::Result; + + serde_conv_doc!( + /// Contains functions to serialize a `u32` as a `i32` and deserialize a `u32` + /// from a `i32`. Errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::{serde_helpers::u32}; + /// # 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!( + /// Contains functions to serialize a `u32` as a `i64` and deserialize a `u32` + /// from a `i64`. Errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::{serde_helpers::u32}; + /// # 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)) + } + ); +} + +#[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}; + use std::result::Result; + + serde_conv_doc!( + /// Contains functions to serialize a `u64` as a `i32` and deserialize a `u64` + /// from a `i32`. Errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::{serde_helpers::u64}; + /// # 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!( + /// Contains functions to serialize a `u64` as a `i64` and deserialize a `u64` + /// from a `i64`. Errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::{serde_helpers::u64}; + /// # 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) => { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index fb4a6185..892b3520 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, bson_datetime, f64, object_id, timestamp}, + serde_helpers::{self, bson_datetime, f64, object_id, timestamp, u32, u64}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -656,55 +656,401 @@ fn test_serialize_deserialize_unsigned_numbers() { fn test_unsigned_helpers() { let _guard = LOCK.run_concurrently(); - #[derive(Serialize)] - 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, - } + #[cfg(feature = "serde_with-3")] + { + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct A { + #[serde_as(as = "u32::AsI32")] + value: u32, - let a = A { num_1: 1, num_2: 2 }; - let doc = serialize_to_document(&a).unwrap(); - assert!(doc.get_i32("num_1").unwrap() == 1); - assert!(doc.get_i32("num_2").unwrap() == 2); + #[serde_as(as = "Option")] + value_optional_none: Option, - let a = A { - num_1: u32::MAX, - num_2: 1, - }; - let doc_result = serialize_to_document(&a); - assert!(doc_result.is_err()); + #[serde_as(as = "Option")] + value_optional_some: Option, - let a = A { - num_1: 1, - num_2: u64::MAX, - }; - let doc_result = serialize_to_document(&a); - assert!(doc_result.is_err()); + #[serde_as(as = "Vec")] + value_vector: Vec, + } - #[derive(Serialize)] - 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, - } + let value = 1; + let a = A { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; - let b = B { - num_1: u32::MAX, - num_2: i64::MAX as u64, - }; - 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); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - let b = B { - num_1: 1, - num_2: i64::MAX as u64 + 1, - }; - let doc_result = serialize_to_document(&b); - assert!(doc_result.is_err()); + // Validate serialized data + assert_eq!( + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." + ); + + 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::Int32(value as i32)), + "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::Int32(value as i32)]; + 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, + "Round-trip failed: deserialized struct did not match original." + ); + + // 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 = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "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("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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct B { + #[serde_as(as = "u32::AsI64")] + value: u32, + + #[serde_as(as = "Option")] + value_optional_none: Option, + + #[serde_as(as = "Option")] + value_optional_some: Option, + + #[serde_as(as = "Vec")] + value_vector: Vec, + } + + let value = u32::MAX; + let b = B { + 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(); + + // Validate serialized data + assert_eq!( + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." + ); + + 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::Int64(value as i64)), + "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::Int64(value as i64)]; + assert_eq!( + 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(); + assert_eq!( + b_deserialized, b, + "Round-trip failed: deserialized struct did not match original." + ); + + // Validate deserialization fails for i64::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i64::MIN; + let bad_b = 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_b); + assert!( + result.is_err(), + "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("Cannot convert i64"), + "Expected error message to mention failed i64 to u32 conversion, got: {}", + err_string + ); + + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct C { + #[serde_as(as = "u64::AsI32")] + value: u64, + + #[serde_as(as = "Option")] + value_optional_none: Option, + + #[serde_as(as = "Option")] + value_optional_some: Option, + + #[serde_as(as = "Vec")] + value_vector: Vec, + } + + let value = 1; + let c = C { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." + ); + + 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::Int32(value as i32)), + "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::Int32(value as i32)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); + + // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); + assert_eq!( + c_deserialized, c, + "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_c: C = C { + 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_c); + 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_c = 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_c); + 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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct D { + #[serde_as(as = "u64::AsI64")] + value: u64, + + #[serde_as(as = "Option")] + value_optional_none: Option, + + #[serde_as(as = "Option")] + value_optional_some: Option, + + #[serde_as(as = "Vec")] + value_vector: Vec, + } + + let value = i64::MAX as u64; + let d = D { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&d).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." + ); + + 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::Int64(value as i64)), + "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::Int64(value as i64)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); + + // Validate deserialized data + let d_deserialized: D = deserialize_from_document(doc).unwrap(); + assert_eq!( + d_deserialized, d, + "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_d: D = D { + 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_d); + 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_d = 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_d); + 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 + ); + } } #[test] @@ -1757,7 +2103,7 @@ fn test_f64_helpers() { assert_eq!( doc.get("value_optional_some"), Some(&Bson::Double(value as f64)), - "Expected serialized value_optional_some to match original time." + "Expected serialized value_optional_some to match original." ); let value_vector = doc @@ -1765,7 +2111,6 @@ fn test_f64_helpers() { .expect("Expected serialized value_vector to be a BSON array."); let expected_value_vector: Vec = vec![Bson::Double(value as f64)]; // TODO: check whether this can be applied to other converters - assert_eq!( value_vector, &expected_value_vector, "Expected each serialized element in value_vector to match the original." @@ -1860,7 +2205,7 @@ fn test_f64_helpers() { assert_eq!( doc.get("value_optional_some"), Some(&Bson::Double(value as f64)), - "Expected serialized value_optional_some to match original time." + "Expected serialized value_optional_some to match original." ); let value_vector = doc From 693faa8cd82d44b236135c2c097771513cc2c6ba Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Thu, 10 Jul 2025 14:52:56 -0400 Subject: [PATCH 09/61] Consolidate module names under u32 and u64 --- src/tests/serde.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 892b3520..fdd77a7c 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, bson_datetime, f64, object_id, timestamp, u32, u64}, + serde_helpers::{self, bson_datetime, object_id, u32, u64}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -1830,16 +1830,16 @@ fn test_timestamp_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct B { - #[serde_as(as = "timestamp::AsU32")] + #[serde_as(as = "u32::FromTimestamp")] pub timestamp: Timestamp, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub timestamp_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub timestamp_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub timestamp_vector: Vec, } @@ -1939,16 +1939,16 @@ fn test_timestamp_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug)] struct A { - #[serde_as(as = "timestamp::FromU32")] + #[serde_as(as = "u32::AsTimestamp")] pub time: u32, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub time_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub time_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub time_vector: Vec, } @@ -2063,16 +2063,16 @@ fn test_f64_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug)] struct A { - #[serde_as(as = "f64::FromU64")] + #[serde_as(as = "u64::AsF64")] pub value: u64, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub value_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub value_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub value_vector: Vec, } @@ -2165,16 +2165,16 @@ fn test_f64_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug)] struct B { - #[serde_as(as = "f64::FromU32")] + #[serde_as(as = "u32::AsF64")] pub value: u32, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub value_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub value_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub value_vector: Vec, } From a9873d3974aa16ed0854fe6d5ca8ef241ff7f918 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Thu, 10 Jul 2025 14:53:23 -0400 Subject: [PATCH 10/61] Consolidate module names under u32 and u64 --- src/serde_helpers.rs | 114 ++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 66 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 1cd06051..45933947 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -261,7 +261,7 @@ pub mod bson_datetime { #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod timestamp { +pub mod u32 { use crate::{macros::serde_conv_doc, Timestamp}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; @@ -276,17 +276,17 @@ pub mod timestamp { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::timestamp, Timestamp}; + /// # use bson::{serde_helpers::u32, Timestamp}; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { - /// #[serde_as(as = "timestamp::AsU32")] + /// #[serde_as(as = "u32::FromTimestamp")] /// pub timestamp: Timestamp, /// } /// # } /// ``` - pub AsU32, + pub FromTimestamp, Timestamp, |ts: &Timestamp| -> Result { if ts.increment != 0 { @@ -307,17 +307,17 @@ pub mod timestamp { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::timestamp; + /// # use bson::serde_helpers::u32; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "timestamp::FromU32")] + /// #[serde_as(as = "u32::AsTimestamp")] /// pub time: u32, /// } /// # } /// ``` - pub FromU32, + pub AsTimestamp, u32, |val: &u32| -> Result { Ok(Timestamp { time: *val, increment: 0 }) @@ -329,53 +329,6 @@ pub mod timestamp { Ok(ts.time) } ); -} - -#[cfg(feature = "serde_with-3")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod f64 { - use crate::macros::serde_conv_doc; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; - - serde_conv_doc!( - /// Contains functions to serialize a `u64` as an `f64` (BSON double) and deserialize a - /// `u64` from an `f64` (BSON double). - /// - /// Deserialization errors if an exact conversion is not possible. - /// - /// ```rust - /// # #[cfg(feature = "serde_with-3")] - /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::f64; - /// # use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct FileInfo { - /// #[serde_as(as = "f64::FromU64")] - /// pub size_bytes: u64, - /// } - /// # } - /// ``` - pub FromU64, - u64, - |val: &u64| -> Result { - if val < &u64::MAX && *val == *val as f64 as u64 { - Ok(*val as f64) - } else { - Err(format!("Cannot convert u64 {} to f64 (BSON double)", val)) - } - }, - |val: f64| -> Result { - if (val - val as u64 as f64).abs() <= f64::EPSILON { - Ok(val as u64) - } else { - Err(format!("Cannot convert f64 (BSON double) {} to u64", val)) - } - } - ); serde_conv_doc!( /// Contains functions to serialize a `u32` as an `f64` (BSON double) and deserialize a @@ -387,17 +340,17 @@ pub mod f64 { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::f64; + /// # use bson::serde_helpers::u32; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct FileInfo { - /// #[serde_as(as = "f64::fromU32")] + /// #[serde_as(as = "u32::AsF64")] /// pub size_bytes: u32, /// } /// # } /// ``` - pub FromU32, + pub AsF64, u32, |val: &u32| -> Result { Ok(*val as f64) @@ -410,15 +363,6 @@ pub mod f64 { } } ); -} - -#[cfg(feature = "serde_with-3")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod u32 { - use crate::macros::serde_conv_doc; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Contains functions to serialize a `u32` as a `i32` and deserialize a `u32` @@ -485,6 +429,44 @@ pub mod u64 { use serde_with::{DeserializeAs, SerializeAs}; use std::result::Result; + serde_conv_doc!( + /// Contains functions to serialize a `u64` as an `f64` (BSON double) and deserialize a + /// `u64` from an `f64` (BSON double). + /// + /// Deserialization errors if an exact conversion is not possible. + /// + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::u64; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct FileInfo { + /// #[serde_as(as = "u64::AsF64")] + /// pub size_bytes: u64, + /// } + /// # } + /// ``` + pub AsF64, + u64, + |val: &u64| -> Result { + if val < &u64::MAX && *val == *val as f64 as u64 { + Ok(*val as f64) + } else { + Err(format!("Cannot convert u64 {} to f64 (BSON double)", val)) + } + }, + |val: f64| -> Result { + if (val - val as u64 as f64).abs() <= f64::EPSILON { + Ok(val as u64) + } else { + Err(format!("Cannot convert f64 (BSON double) {} to u64", val)) + } + } + ); + serde_conv_doc!( /// Contains functions to serialize a `u64` as a `i32` and deserialize a `u64` /// from a `i32`. Errors if an exact conversion is not possible. From b9b08cf666a8d1ce5f4ac6415e5d6d97b2b254e1 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 09:39:01 -0400 Subject: [PATCH 11/61] Refactor uuid to binary converter to use serde_conv --- src/serde_helpers.rs | 128 +++++++++++++++++++++++-------------------- src/tests/serde.rs | 66 +++++++++++++++++++--- src/uuid.rs | 7 ++- 3 files changed, 131 insertions(+), 70 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 45933947..7d834017 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -8,12 +8,6 @@ use std::{ use serde::{de::Visitor, Deserialize, Serialize, Serializer}; -#[cfg(feature = "uuid-1")] -#[doc(inline)] -pub use uuid_1_as_binary::{ - deserialize as deserialize_uuid_1_from_binary, - serialize as serialize_uuid_1_as_binary, -}; #[cfg(feature = "uuid-1")] #[doc(inline)] pub use uuid_1_as_c_sharp_legacy_binary::{ @@ -222,8 +216,8 @@ pub mod bson_datetime { /// ``` pub FromI64, i64, - |val: &i64| -> Result { - Ok(DateTime::from_millis(*val)) + |value: &i64| -> Result { + Ok(DateTime::from_millis(*value)) }, |date: DateTime| -> Result { Ok(date.timestamp_millis()) @@ -250,8 +244,8 @@ pub mod bson_datetime { /// ``` pub FromTime03OffsetDateTime, time::OffsetDateTime, - |val: &time::OffsetDateTime| -> Result { - Ok(DateTime::from_time_0_3(*val)) + |value: &time::OffsetDateTime| -> Result { + Ok(DateTime::from_time_0_3(*value)) }, |date: DateTime| -> Result { Ok(date.to_time_0_3()) @@ -288,14 +282,14 @@ pub mod u32 { /// ``` pub FromTimestamp, Timestamp, - |ts: &Timestamp| -> Result { - if ts.increment != 0 { - return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", ts)); + |timestamp: &Timestamp| -> Result { + if timestamp.increment != 0 { + return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", timestamp)); } - Ok(ts.time) + Ok(timestamp.time) }, - |val: u32| -> Result { - Ok(Timestamp { time: val, increment: 0 }) + |value: u32| -> Result { + Ok(Timestamp { time: value, increment: 0 }) } ); @@ -319,14 +313,14 @@ pub mod u32 { /// ``` pub AsTimestamp, u32, - |val: &u32| -> Result { - Ok(Timestamp { time: *val, increment: 0 }) + |value: &u32| -> Result { + Ok(Timestamp { time: *value, increment: 0 }) }, - |ts: Timestamp| -> Result { - if ts.increment != 0 { - return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", ts)); + |timestamp: Timestamp| -> Result { + if timestamp.increment != 0 { + return Err(format!("Cannot convert Timestamp with a non-zero increment to u32: {:?}", timestamp)); } - Ok(ts.time) + Ok(timestamp.time) } ); @@ -352,14 +346,14 @@ pub mod u32 { /// ``` pub AsF64, u32, - |val: &u32| -> Result { - Ok(*val as f64) + |value: &u32| -> Result { + Ok(*value as f64) }, - |val: f64| -> Result { - if (val - val as u32 as f64).abs() <= f64::EPSILON { - Ok(val as u32) + |value: f64| -> Result { + if (value - value as u32 as f64).abs() <= f64::EPSILON { + Ok(value as u32) } else { - Err(format!("Cannot convert f64 (BSON double) {} to u32", val)) + Err(format!("Cannot convert f64 (BSON double) {} to u32", value)) } } ); @@ -451,18 +445,18 @@ pub mod u64 { /// ``` pub AsF64, u64, - |val: &u64| -> Result { - if val < &u64::MAX && *val == *val as f64 as u64 { - Ok(*val as f64) + |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 (BSON double)", val)) + Err(format!("Cannot convert u64 {} to f64 (BSON double)", value)) } }, - |val: f64| -> Result { - if (val - val as u64 as f64).abs() <= f64::EPSILON { - Ok(val as u64) + |value: f64| -> Result { + if (value - value as u64 as f64).abs() <= f64::EPSILON { + Ok(value as u64) } else { - Err(format!("Cannot convert f64 (BSON double) {} to u64", val)) + Err(format!("Cannot convert f64 (BSON double) {} to u64", value)) } } ); @@ -524,6 +518,45 @@ pub mod u64 { ); } +#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "serde_with-3", feature = "uuid-1"))))] +pub mod uuid_1 { + use crate::macros::serde_conv_doc; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + use std::result::Result; + use uuid::Uuid; + + serde_conv_doc!( + /// Contains functions to serialize a [`uuid::Uuid`] as a [`crate::Binary`] and deserialize a + /// [`uuid::Uuid`] from a [`crate::Binary`]. + /// + /// ```rust + /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] + /// # { + /// use serde::{Serialize, Deserialize}; + /// use uuid::Uuid; + /// use bson::serde_helpers::uuid_1; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde(with = "uuid_1::AsBinary")] + /// pub id: Uuid, + /// } + /// # } + /// ``` + pub AsBinary, + Uuid, + |uuid: &Uuid| -> Result { + Ok(crate::uuid::Uuid::from(*uuid)) + }, + |bson_uuid: crate::uuid::Uuid| -> Result { + Ok(bson_uuid.into()) + } + ); +} + #[allow(unused_macros)] macro_rules! as_binary_mod { ($feat:meta, $uu:path) => { @@ -549,29 +582,6 @@ macro_rules! as_binary_mod { }; } -/// Contains functions to serialize a [`uuid::Uuid`] as a [`crate::Binary`] and deserialize a -/// [`uuid::Uuid`] from a [`crate::Binary`]. -/// -/// ```rust -/// # #[cfg(feature = "uuid-1")] -/// # { -/// use serde::{Serialize, Deserialize}; -/// use uuid::Uuid; -/// use bson::serde_helpers::uuid_1_as_binary; -/// -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "uuid_1_as_binary")] -/// pub id: Uuid, -/// } -/// # } -/// ``` -#[cfg(feature = "uuid-1")] -#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] -pub mod uuid_1_as_binary { - as_binary_mod!(cfg(feature = "uuid-1"), uuid::Uuid); -} - #[allow(unused_macros)] macro_rules! as_legacy_binary_mod { ($feat:meta, $uu:path, $rep:path) => { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index fdd77a7c..8353e26f 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1794,31 +1794,81 @@ fn test_oid_helpers() { } #[test] -#[cfg(feature = "uuid-1")] +#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] fn test_uuid_1_helpers() { - use serde_helpers::uuid_1_as_binary; + use serde_helpers::uuid_1; use uuid::Uuid; let _guard = LOCK.run_concurrently(); - #[derive(Serialize, Deserialize)] + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] struct A { - #[serde(with = "uuid_1_as_binary")] + #[serde_as(as = "uuid_1::AsBinary")] uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, } let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let a = A { uuid }; + let a: A = A { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data match doc.get("uuid").unwrap() { Bson::Binary(bin) => { assert_eq!(bin.subtype, BinarySubtype::Uuid); assert_eq!(bin.bytes, uuid.as_bytes()); } - _ => panic!("expected Bson::Binary"), + _ => panic!("Expected serialized uuid to match original as Bson::Binary"), } - let a: A = deserialize_from_document(doc).unwrap(); - assert_eq!(a.uuid, uuid); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + match doc.get("uuid_optional_some") { + Some(Bson::Binary(bin)) => { + assert_eq!(bin.subtype, BinarySubtype::Uuid); + assert_eq!(bin.bytes, uuid.as_bytes()); + } + _ => panic!("Expected serialized uuid_optional_some to be Bson::Binary."), + } + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected serialized uuid_vector to be a BSON array."); + let expected_uuid_vector: Vec = vec![Bson::Binary(Binary { + subtype: BinarySubtype::Uuid, + bytes: uuid.as_bytes().to_vec(), + })]; + assert_eq!( + uuid_vector, &expected_uuid_vector, + "Expected each serialized element in uuid_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] diff --git a/src/uuid.rs b/src/uuid.rs index c747fffb..3bf464ed 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -19,12 +19,13 @@ //! e.g. //! //! ``` rust -//! # #[cfg(feature = "uuid-1")] +//! # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] //! # { //! # use uuid as uuid; //! use serde::{Serialize, Deserialize}; //! use bson::doc; //! +//! #[serde_as] //! #[derive(Serialize, Deserialize)] //! struct Foo { //! /// serializes as a String or subtype 0 BSON binary, depending @@ -35,8 +36,8 @@ //! bson_uuid: bson::Uuid, //! //! /// serializes as a BSON binary with subtype 4 when either is used. -//! /// this requires the "uuid-1" feature flag -//! #[serde(with = "bson::serde_helpers::uuid_1_as_binary")] +//! /// this requires the "uuid-1" and "serde_with-3" feature flags +//! #[serde_as(as = "bson::serde_helpers::uuid_1::AsBinary")] //! uuid_as_bson: uuid::Uuid, //! } //! # }; From 1ccea328829a8e5927a271934ab8343cdcfe1dcb Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 10:05:10 -0400 Subject: [PATCH 12/61] Reorder tests based on serde_helpers ordering --- src/tests/serde.rs | 1488 ++++++++++++++++++++++---------------------- 1 file changed, 739 insertions(+), 749 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 8353e26f..e5d0436b 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -653,33 +653,33 @@ fn test_serialize_deserialize_unsigned_numbers() { } #[test] -fn test_unsigned_helpers() { +fn test_oid_helpers() { let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] + #[derive(Serialize, Deserialize)] struct A { - #[serde_as(as = "u32::AsI32")] - value: u32, + #[serde_as(as = "object_id::FromHexString")] + oid: String, - #[serde_as(as = "Option")] - value_optional_none: Option, + #[serde_as(as = "Option")] + oid_optional_none: Option, - #[serde_as(as = "Option")] - value_optional_some: Option, + #[serde_as(as = "Option")] + oid_optional_some: Option, - #[serde_as(as = "Vec")] - value_vector: Vec, + #[serde_as(as = "Vec")] + oid_vector: Vec, } - let value = 1; + let oid = ObjectId::new(); let a = A { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], + oid: oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(oid.to_string()), + oid_vector: vec![oid.to_string()], }; // Serialize the struct to BSON @@ -687,102 +687,106 @@ fn test_unsigned_helpers() { // Validate serialized data assert_eq!( - doc.get_i32("value").unwrap(), - value as i32, - "Expected serialized value to match original." + doc.get_object_id("oid").unwrap(), + oid, + "Expected serialized oid to match original ObjectId." ); assert_eq!( - doc.get("value_optional_none"), + doc.get("oid_optional_none"), Some(&Bson::Null), - "Expected serialized value_optional_none to be None." + "Expected serialized oid_optional_none to be None." ); - assert_eq!( - doc.get("value_optional_some"), - Some(&Bson::Int32(value as i32)), - "Expected serialized value_optional_some to match original." - ); + match doc.get("oid_optional_some") { + Some(Bson::ObjectId(value)) => { + assert_eq!( + *value, oid, + "Expected serialized oid_optional_some to match original ObjectId." + ) + } + _ => { + panic!("Expected serialized oid_optional_some to be a BSON ObjectId.") + } + } - 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)]; + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = a + .oid_vector + .into_iter() + .map(|oid| Bson::ObjectId(ObjectId::parse_str(oid).unwrap())) + .collect(); assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the original." + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_vector match the original." ); - // Validate deserialized data + // Deserialize the BSON back to the struct let a_deserialized: A = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data assert_eq!( - a_deserialized, a, - "Round-trip failed: deserialized struct did not match original." + a_deserialized.oid, + oid.to_string(), + "Expected deserialized oid to match the original." ); - // 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 = serialize_to_document(&bad_a); - assert!( - result.is_err(), - "Serialization should fail for u32::MAX since it can't be exactly represented as i32" + assert_eq!( + a_deserialized.oid_optional_some, + Some(oid.to_string()), + "Expected deserialized oid_optional_some to match the original." ); - let err_string = format!("{:?}", result.unwrap_err()); - assert!( - err_string.contains("Cannot convert u32"), - "Expected error message to mention failed u32 to i32 conversion, got: {}", - err_string + + assert_eq!( + a_deserialized.oid_vector, + vec![oid.to_string()], + "Expected deserialized oid_vector to match the original." ); - // 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], + // Validate serializing error case with an invalid ObjectId string + let invalid_oid = "invalid_oid"; + let bad_a = A { + oid: invalid_oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(invalid_oid.to_string()), + oid_vector: vec![invalid_oid.to_string()], }; - let result: Result = deserialize_from_document(bad_a); + let result = serialize_to_document(&bad_a); assert!( result.is_err(), - "Deserialization should fail for i32::MIN since it can't be exactly represented as u32" + "Serialization should fail for invalid ObjectId strings" ); 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.contains("BSON error"), + "Expected error message to mention BSON error: {}", err_string ); #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] + #[derive(Serialize, Deserialize, Debug)] struct B { - #[serde_as(as = "u32::AsI64")] - value: u32, + #[serde_as(as = "object_id::AsHexString")] + oid: ObjectId, - #[serde_as(as = "Option")] - value_optional_none: Option, + #[serde_as(as = "Option")] + oid_optional_none: Option, - #[serde_as(as = "Option")] - value_optional_some: Option, + #[serde_as(as = "Option")] + oid_optional_some: Option, - #[serde_as(as = "Vec")] - value_vector: Vec, + #[serde_as(as = "Vec")] + oid_vector: Vec, } - let value = u32::MAX; + let oid = ObjectId::new(); let b = B { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], + oid, + oid_optional_none: None, + oid_optional_some: Some(oid), + oid_vector: vec![oid], }; // Serialize the struct to BSON @@ -790,272 +794,93 @@ fn test_unsigned_helpers() { // Validate serialized data assert_eq!( - doc.get_i64("value").unwrap(), - value as i64, - "Expected serialized value to match original." + doc.get_str("oid").unwrap(), + oid.to_hex(), + "Expected serialized oid to match original ObjectId as hex string." ); assert_eq!( - doc.get("value_optional_none"), + doc.get("oid_optional_none"), Some(&Bson::Null), - "Expected serialized value_optional_none to be None." + "Expected serialized oid_optional_none to be None." ); + match doc.get("oid_optional_some") { + Some(Bson::String(value)) => { + assert_eq!( + *value, + oid.to_hex(), + "Expected serialized oid_optional_some to match original ObjectId." + ) + } + _ => { + panic!("Expected serialized oid_optional_some to be a BSON String.") + } + } + + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = b + .oid_vector + .into_iter() + .map(|oid| Bson::String(oid.to_hex())) + .collect(); assert_eq!( - doc.get("value_optional_some"), - Some(&Bson::Int64(value as i64)), - "Expected serialized value_optional_some to match original." + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_vector match the 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::Int64(value as i64)]; + // Deserialize the BSON back to the struct + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the original." + b_deserialized.oid, oid, + "Expected deserialized oid to match the original." ); - // Validate deserialized data - let b_deserialized: B = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized, b, - "Round-trip failed: deserialized struct did not match original." + b_deserialized.oid_optional_none, None, + "Expected deserialized oid_optional_none to be None." ); - // Validate deserialization fails for i64::MIN because negative values can't be converted to - // u32 - let invalid_value_for_deserializing = i64::MIN; - let bad_b = 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], + assert_eq!( + b_deserialized.oid_optional_some, + Some(oid), + "Expected deserialized oid_optional_some to match the original." + ); + + assert_eq!( + b_deserialized.oid_vector, + vec![oid], + "Expected deserialized oid_vector to match the original.." + ); + + // Validate deserializing error case with an invalid ObjectId string + let invalid_doc = doc! { + "oid": "not_a_valid_oid", + "oid_optional_none": Bson::Null, + "oid_optional_some": "also_invalid_oid", + "oid_vector": ["bad1", "bad2"] }; - let result: Result = deserialize_from_document(bad_b); + let result: Result = deserialize_from_document(invalid_doc); assert!( result.is_err(), - "Deserialization should fail for i64::MIN since it can't be exactly represented as u32" + "Deserialization should fail for invalid ObjectId strings" ); let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("Cannot convert i64"), - "Expected error message to mention failed i64 to u32 conversion, got: {}", + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", err_string ); + } +} - #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct C { - #[serde_as(as = "u64::AsI32")] - value: u64, - - #[serde_as(as = "Option")] - value_optional_none: Option, - - #[serde_as(as = "Option")] - value_optional_some: Option, - - #[serde_as(as = "Vec")] - value_vector: Vec, - } - - let value = 1; - let c = C { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_i32("value").unwrap(), - value as i32, - "Expected serialized value to match original." - ); - - 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::Int32(value as i32)), - "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::Int32(value as i32)]; - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the original." - ); - - // Validate deserialized data - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - assert_eq!( - c_deserialized, c, - "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_c: C = C { - 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_c); - 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_c = 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_c); - 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 - ); - - #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct D { - #[serde_as(as = "u64::AsI64")] - value: u64, - - #[serde_as(as = "Option")] - value_optional_none: Option, - - #[serde_as(as = "Option")] - value_optional_some: Option, - - #[serde_as(as = "Vec")] - value_vector: Vec, - } - - let value = i64::MAX as u64; - let d = D { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&d).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_i64("value").unwrap(), - value as i64, - "Expected serialized value to match original." - ); - - 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::Int64(value as i64)), - "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::Int64(value as i64)]; - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the original." - ); - - // Validate deserialized data - let d_deserialized: D = deserialize_from_document(doc).unwrap(); - assert_eq!( - d_deserialized, d, - "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_d: D = D { - 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_d); - 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_d = 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_d); - 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 - ); - } -} - -#[test] -fn test_datetime_helpers() { - let _guard = LOCK.run_concurrently(); +#[test] +fn test_bson_datetime_helpers() { + let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] { @@ -1167,44 +992,38 @@ fn test_datetime_helpers() { "Expected error message to mention BSON error: {}", err_string ); - } - - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - { - use std::str::FromStr; #[serde_as] - #[derive(Deserialize, Serialize)] - struct A { - #[serde_as(as = "bson_datetime::FromChronoDateTime")] - pub date: chrono::DateTime, + #[derive(Serialize, Deserialize)] + struct B { + #[serde_as(as = "bson_datetime::FromI64")] + date: i64, - #[serde_as(as = "Option")] - pub date_optional_none: Option>, + #[serde_as(as = "Option")] + date_optional_none: Option, - #[serde_as(as = "Option")] - pub date_optional_some: Option>, + #[serde_as(as = "Option")] + date_optional_some: Option, - #[serde_as(as = "Vec")] - pub date_vector: Vec>, + #[serde_as(as = "Vec")] + 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, + let date = DateTime::now(); + let b = B { + date: date.timestamp_millis(), date_optional_none: None, - date_optional_some: Some(date), - date_vector: vec![date], + 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(); + let doc = serialize_to_document(&b).unwrap(); // Validate serialized data assert_eq!( - doc.get_datetime("date").unwrap().to_chrono(), - date, + doc.get_datetime("date").unwrap(), + &date, "Expected serialized date to match original date." ); @@ -1217,8 +1036,7 @@ fn test_datetime_helpers() { match doc.get("date_optional_some") { Some(Bson::DateTime(value)) => { assert_eq!( - *value, - DateTime::from_chrono(date), + *value, date, "Expected serialized date_optional_some to match original." ) } @@ -1228,46 +1046,43 @@ fn test_datetime_helpers() { let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = a + let expected_date_vector: Vec = b .date_vector .into_iter() - .map(|dt| Bson::DateTime(dt.into())) + .map(|dt| Bson::DateTime(DateTime::from_millis(dt))) .collect(); assert_eq!( date_vector, &expected_date_vector, - "Expected each serialized element in date_vector to be a BSON DateTime matching the \ - original." + "Expected each serialized element in date_vector match the original." ); // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); + let b_deserialized: B = deserialize_from_document(doc).unwrap(); // Validate deserialized data assert_eq!( - a_deserialized.date, date, + b_deserialized.date, + date.timestamp_millis(), "Expected deserialized date to match original." ); assert_eq!( - a_deserialized.date_optional_none, None, + b_deserialized.date_optional_none, None, "Expected deserialized date_optional_none to be None." ); assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." + b_deserialized.date_optional_some, + Some(date.timestamp_millis()), + "Expected deserialized date_optional_some to match original." ); assert_eq!( - a_deserialized.date_vector, - vec![date], + b_deserialized.date_vector, + vec![date.timestamp_millis()], "Expected deserialized date_vector to match original." ); - } - #[cfg(feature = "serde_with-3")] - { #[serde_as] #[derive(Deserialize, Serialize)] struct C { @@ -1379,30 +1194,33 @@ fn test_datetime_helpers() { ); } - #[cfg(feature = "serde_with-3")] + #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] { + use std::str::FromStr; + #[serde_as] - #[derive(Serialize, Deserialize)] + #[derive(Deserialize, Serialize)] struct A { - #[serde_as(as = "bson_datetime::FromI64")] - date: i64, + #[serde_as(as = "bson_datetime::FromChronoDateTime")] + pub date: chrono::DateTime, - #[serde_as(as = "Option")] - date_optional_none: Option, + #[serde_as(as = "Option")] + pub date_optional_none: Option>, - #[serde_as(as = "Option")] - date_optional_some: Option, + #[serde_as(as = "Option")] + pub date_optional_some: Option>, - #[serde_as(as = "Vec")] - date_vector: Vec, + #[serde_as(as = "Vec")] + pub date_vector: Vec>, } - let date = DateTime::now(); - let a = A { - date: date.timestamp_millis(), + 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.timestamp_millis()), - date_vector: vec![date.timestamp_millis()], + date_optional_some: Some(date), + date_vector: vec![date], }; // Serialize the struct to BSON @@ -1410,8 +1228,8 @@ fn test_datetime_helpers() { // Validate serialized data assert_eq!( - doc.get_datetime("date").unwrap(), - &date, + doc.get_datetime("date").unwrap().to_chrono(), + date, "Expected serialized date to match original date." ); @@ -1424,7 +1242,8 @@ fn test_datetime_helpers() { match doc.get("date_optional_some") { Some(Bson::DateTime(value)) => { assert_eq!( - *value, date, + *value, + DateTime::from_chrono(date), "Expected serialized date_optional_some to match original." ) } @@ -1437,11 +1256,12 @@ fn test_datetime_helpers() { let expected_date_vector: Vec = a .date_vector .into_iter() - .map(|dt| Bson::DateTime(DateTime::from_millis(dt))) + .map(|dt| Bson::DateTime(dt.into())) .collect(); assert_eq!( date_vector, &expected_date_vector, - "Expected each serialized element in date_vector match the original." + "Expected each serialized element in date_vector to be a BSON DateTime matching the \ + original." ); // Deserialize the BSON back to the struct @@ -1449,8 +1269,7 @@ fn test_datetime_helpers() { // Validate deserialized data assert_eq!( - a_deserialized.date, - date.timestamp_millis(), + a_deserialized.date, date, "Expected deserialized date to match original." ); @@ -1461,13 +1280,13 @@ fn test_datetime_helpers() { assert_eq!( a_deserialized.date_optional_some, - Some(date.timestamp_millis()), - "Expected deserialized date_optional_some to match original." + Some(date), + "Expected deserialized date_optional_some to match the original." ); assert_eq!( a_deserialized.date_vector, - vec![date.timestamp_millis()], + vec![date], "Expected deserialized date_vector to match original." ); } @@ -1568,33 +1387,34 @@ fn test_datetime_helpers() { } #[test] -fn test_oid_helpers() { +fn test_u32_helpers() { let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Serialize, Deserialize)] + #[derive(Deserialize, Serialize)] struct A { - #[serde_as(as = "object_id::FromHexString")] - oid: String, + #[serde_as(as = "u32::FromTimestamp")] + pub timestamp: Timestamp, - #[serde_as(as = "Option")] - oid_optional_none: Option, + #[serde_as(as = "Option")] + pub timestamp_optional_none: Option, - #[serde_as(as = "Option")] - oid_optional_some: Option, + #[serde_as(as = "Option")] + pub timestamp_optional_some: Option, - #[serde_as(as = "Vec")] - oid_vector: Vec, + #[serde_as(as = "Vec")] + pub timestamp_vector: Vec, } - let oid = ObjectId::new(); + let time = 12345; + let timestamp = Timestamp { time, increment: 0 }; let a = A { - oid: oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(oid.to_string()), - oid_vector: vec![oid.to_string()], + timestamp, + timestamp_optional_none: None, + timestamp_optional_some: Some(timestamp), + timestamp_vector: vec![timestamp], }; // Serialize the struct to BSON @@ -1602,40 +1422,34 @@ fn test_oid_helpers() { // Validate serialized data assert_eq!( - doc.get_object_id("oid").unwrap(), - oid, - "Expected serialized oid to match original ObjectId." + doc.get("timestamp").unwrap(), + &Bson::Int64(time as i64), + "Expected serialized time to match the original." ); assert_eq!( - doc.get("oid_optional_none"), + doc.get("timestamp_optional_none"), Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." + "Expected serialized timestamp_optional_none to be None." ); - match doc.get("oid_optional_some") { - Some(Bson::ObjectId(value)) => { - assert_eq!( - *value, oid, - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON ObjectId.") - } - } + assert_eq!( + doc.get("timestamp_optional_some"), + Some(&Bson::Int64(time as i64)), + "Expected serialized timestamp_optional_some to match original time." + ); - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = a - .oid_vector - .into_iter() - .map(|oid| Bson::ObjectId(ObjectId::parse_str(oid).unwrap())) + let timestamp_vector = doc + .get_array("timestamp_vector") + .expect("Expected serialized timestamp_vector to be a BSON array."); + let expected_timestamp_vector: Vec = a + .timestamp_vector + .iter() + .map(|ts| Bson::Int64(ts.time as i64)) .collect(); assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_vector match the original." + timestamp_vector, &expected_timestamp_vector, + "Expected each serialized element in timestamp_vector to match the original." ); // Deserialize the BSON back to the struct @@ -1643,65 +1457,72 @@ fn test_oid_helpers() { // Validate deserialized data assert_eq!( - a_deserialized.oid, - oid.to_string(), - "Expected deserialized oid to match the original." + a_deserialized.timestamp, timestamp, + "Expected deserialized timestamp to match the original." ); assert_eq!( - a_deserialized.oid_optional_some, - Some(oid.to_string()), - "Expected deserialized oid_optional_some to match the original." + a_deserialized.timestamp_optional_none, None, + "Expected deserialized timestamp_optional_none to be None." ); assert_eq!( - a_deserialized.oid_vector, - vec![oid.to_string()], - "Expected deserialized oid_vector to match the original." + a_deserialized.timestamp_optional_some, + Some(timestamp), + "Expected deserialized timestamp_optional_some to match the original." ); - // Validate serializing error case with an invalid ObjectId string - let invalid_oid = "invalid_oid"; - let bad_a = A { - oid: invalid_oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(invalid_oid.to_string()), - oid_vector: vec![invalid_oid.to_string()], + assert_eq!( + a_deserialized.timestamp_vector, + vec![timestamp], + "Expected deserialized timestamp_vector to match the original." + ); + + // Validate serializing error case with an invalid Timestamp + let invalid_timestamp_for_serializing = Timestamp { + time: 0, + increment: 2, + }; + 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 invalid ObjectId strings" + "Serialization should fail for Timestamp with increment != 0" ); 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 Timestamp with a non-zero increment to u32"), + "Expected error message to mention non-zero increment: {}", err_string ); #[serde_as] - #[derive(Serialize, Deserialize, Debug)] + #[derive(Deserialize, Serialize, Debug)] struct B { - #[serde_as(as = "object_id::AsHexString")] - oid: ObjectId, + #[serde_as(as = "u32::AsTimestamp")] + pub time: u32, - #[serde_as(as = "Option")] - oid_optional_none: Option, + #[serde_as(as = "Option")] + pub time_optional_none: Option, - #[serde_as(as = "Option")] - oid_optional_some: Option, + #[serde_as(as = "Option")] + pub time_optional_some: Option, - #[serde_as(as = "Vec")] - oid_vector: Vec, + #[serde_as(as = "Vec")] + pub time_vector: Vec, } - let oid = ObjectId::new(); + let time = 12345; let b = B { - oid, - oid_optional_none: None, - oid_optional_some: Some(oid), - oid_vector: vec![oid], + time, + time_optional_none: None, + time_optional_some: Some(time), + time_vector: vec![time], }; // Serialize the struct to BSON @@ -1709,41 +1530,44 @@ fn test_oid_helpers() { // Validate serialized data assert_eq!( - doc.get_str("oid").unwrap(), - oid.to_hex(), - "Expected serialized oid to match original ObjectId as hex string." + doc.get_timestamp("time").unwrap(), + Timestamp { time, increment: 0 }, + "Expected serialized time to match the original." ); assert_eq!( - doc.get("oid_optional_none"), + doc.get("time_optional_none"), Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." + "Expected serialized time_optional_none to be None." ); - match doc.get("oid_optional_some") { - Some(Bson::String(value)) => { + match doc.get("time_optional_some") { + Some(Bson::Timestamp(ts)) => { assert_eq!( - *value, - oid.to_hex(), - "Expected serialized oid_optional_some to match original ObjectId." + *ts, + Timestamp { time, increment: 0 }, + "Expected serialized time_optional_some to match original time." ) } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON String.") - } + _ => panic!("Expected serialized time_optional_some to be a BSON Timestamp."), } - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = b - .oid_vector - .into_iter() - .map(|oid| Bson::String(oid.to_hex())) + let time_vector = doc + .get_array("time_vector") + .expect("Expected serialized time_vector to be a BSON array."); + let expected_time_vector: Vec = b + .time_vector + .iter() + .map(|val| { + Bson::Timestamp(Timestamp { + time: *val, + increment: 0, + }) + }) .collect(); assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_vector match the original." + time_vector, &expected_time_vector, + "Expected each serialized element in time_vector to match the original." ); // Deserialize the BSON back to the struct @@ -1751,363 +1575,324 @@ fn test_oid_helpers() { // Validate deserialized data assert_eq!( - b_deserialized.oid, oid, - "Expected deserialized oid to match the original." + b_deserialized.time, time, + "Expected deserialized time to match the original." ); assert_eq!( - b_deserialized.oid_optional_none, None, - "Expected deserialized oid_optional_none to be None." + b_deserialized.time_optional_none, None, + "Expected deserialized time_optional_none to be None." ); assert_eq!( - b_deserialized.oid_optional_some, - Some(oid), - "Expected deserialized oid_optional_some to match the original." + b_deserialized.time_optional_some, + Some(time), + "Expected deserialized time_optional_some to match the original." ); assert_eq!( - b_deserialized.oid_vector, - vec![oid], - "Expected deserialized oid_vector to match the original.." + b_deserialized.time_vector, + vec![time], + "Expected deserialized time_vector to match the original." ); - // Validate deserializing error case with an invalid ObjectId string + // Validate deserializing error case with an invalid Timestamp + let invalid_timestamp_for_deserializing = Timestamp { + time: 0, + increment: 2, + }; let invalid_doc = doc! { - "oid": "not_a_valid_oid", - "oid_optional_none": Bson::Null, - "oid_optional_some": "also_invalid_oid", - "oid_vector": ["bad1", "bad2"] + "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 invalid ObjectId strings" + "Deserialization should fail for Timestamp with increment != 0" ); 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 Timestamp with a non-zero increment to u32"), + "Expected error message to mention non-zero increment: {}", err_string ); - } -} -#[test] -#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] -fn test_uuid_1_helpers() { - use serde_helpers::uuid_1; - use uuid::Uuid; + #[serde_as] + #[derive(Deserialize, Serialize, Debug)] + struct C { + #[serde_as(as = "u32::AsF64")] + pub value: u32, - let _guard = LOCK.run_concurrently(); + #[serde_as(as = "Option")] + pub value_optional_none: Option, - #[serde_as] - #[derive(Serialize, Deserialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "uuid_1::AsBinary")] - uuid: Uuid, + #[serde_as(as = "Option")] + pub value_optional_some: Option, - #[serde_as(as = "Option")] - uuid_optional_none: Option, + #[serde_as(as = "Vec")] + pub value_vector: Vec, + } - #[serde_as(as = "Option")] - uuid_optional_some: Option, + let value = 12345; + let c = C { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; - #[serde_as(as = "Vec")] - uuid_vector: Vec, - } + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); - let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let a: A = A { - uuid, - uuid_optional_none: None, - uuid_optional_some: Some(uuid), - uuid_vector: vec![uuid], - }; + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); + assert_eq!( + doc.get("value_optional_none"), + Some(&Bson::Null), + "Expected serialized value_optional_none to be None." + ); - // Validate serialized data - match doc.get("uuid").unwrap() { - Bson::Binary(bin) => { - assert_eq!(bin.subtype, BinarySubtype::Uuid); - assert_eq!(bin.bytes, uuid.as_bytes()); - } - _ => panic!("Expected serialized uuid to match original as Bson::Binary"), - } + assert_eq!( + doc.get("value_optional_some"), + Some(&Bson::Double(value as f64)), + "Expected serialized value_optional_some to match original." + ); - assert_eq!( - doc.get("uuid_optional_none"), - Some(&Bson::Null), - "Expected serialized uuid_optional_none to be None." - ); + 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)]; - match doc.get("uuid_optional_some") { - Some(Bson::Binary(bin)) => { - assert_eq!(bin.subtype, BinarySubtype::Uuid); - assert_eq!(bin.bytes, uuid.as_bytes()); - } - _ => panic!("Expected serialized uuid_optional_some to be Bson::Binary."), - } + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); - let uuid_vector = doc - .get_array("uuid_vector") - .expect("Expected serialized uuid_vector to be a BSON array."); - let expected_uuid_vector: Vec = vec![Bson::Binary(Binary { - subtype: BinarySubtype::Uuid, - bytes: uuid.as_bytes().to_vec(), - })]; - assert_eq!( - uuid_vector, &expected_uuid_vector, - "Expected each serialized element in uuid_vector to match the original." - ); + // Deserialize the BSON back to the struct + let c_deserialized: C = deserialize_from_document(doc).unwrap(); - // Validate deserialized data - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - assert_eq!( - a_deserialized, a, - "Deserialized struct does not match original." - ); -} + // Validate deserialized data + assert_eq!( + c_deserialized.value, value, + "Expected deserialized value to match the original." + ); -#[test] -fn test_timestamp_helpers() { - let _guard = LOCK.run_concurrently(); + assert_eq!( + c_deserialized.value_optional_none, None, + "Expected deserialized val_optional_none to be None." + ); + + assert_eq!( + c_deserialized.value_optional_some, + Some(value), + "Expected deserialized val_optional_some to match the original." + ); + + assert_eq!( + c_deserialized.value_vector, + vec![value], + "Expected deserialized val_vector to match the original." + ); - #[cfg(feature = "serde_with-3")] - { #[serde_as] - #[derive(Deserialize, Serialize)] - struct B { - #[serde_as(as = "u32::FromTimestamp")] - pub timestamp: Timestamp, + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct D { + #[serde_as(as = "u32::AsI32")] + value: u32, - #[serde_as(as = "Option")] - pub timestamp_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub timestamp_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub timestamp_vector: Vec, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let time = 12345; - let timestamp = Timestamp { time, increment: 0 }; - let b = B { - timestamp, - timestamp_optional_none: None, - timestamp_optional_some: Some(timestamp), - timestamp_vector: vec![timestamp], + let value = 1; + let d = D { + 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(&d).unwrap(); // Validate serialized data assert_eq!( - doc.get("timestamp").unwrap(), - &Bson::Int64(time as i64), - "Expected serialized time to match the original." + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." ); assert_eq!( - doc.get("timestamp_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized timestamp_optional_none to be None." + "Expected serialized value_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." + doc.get("value_optional_some"), + Some(&Bson::Int32(value as i32)), + "Expected serialized value_optional_some to match original." ); - let timestamp_vector = doc - .get_array("timestamp_vector") - .expect("Expected serialized timestamp_vector to be a BSON array."); - let expected_timestamp_vector: Vec = b - .timestamp_vector - .iter() - .map(|ts| Bson::Int64(ts.time as i64)) - .collect(); + 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!( - timestamp_vector, &expected_timestamp_vector, - "Expected each serialized element in timestamp_vector to match the original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let d_deserialized: D = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.timestamp, timestamp, - "Expected deserialized timestamp to match the original." - ); - - assert_eq!( - b_deserialized.timestamp_optional_none, None, - "Expected deserialized timestamp_optional_none to be None." + d_deserialized, d, + "Round-trip failed: deserialized struct did not match original." ); - assert_eq!( - b_deserialized.timestamp_optional_some, - Some(timestamp), - "Expected deserialized timestamp_optional_some to match the original." + // Validate serialization fails because u32::MAX is too large to fit in i32 + let invalid_value_for_serializing = u32::MAX; + let bad_d: D = D { + 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_d); + assert!( + result.is_err(), + "Serialization should fail for u32::MAX since it can't be exactly represented as i32" ); - - assert_eq!( - b_deserialized.timestamp_vector, - vec![timestamp], - "Expected deserialized timestamp_vector to match the original." + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("Cannot convert u32"), + "Expected error message to mention failed u32 to i32 conversion, got: {}", + err_string ); - // Validate serializing error case with an invalid Timestamp - let invalid_timestamp_for_serializing = Timestamp { - time: 0, - increment: 2, - }; - let bad_b: B = B { - timestamp: invalid_timestamp_for_serializing, - timestamp_optional_none: None, - timestamp_optional_some: Some(invalid_timestamp_for_serializing), - timestamp_vector: vec![invalid_timestamp_for_serializing], + // Validate deserialization fails for i32::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i32::MIN; + let bad_d = 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_d); assert!( result.is_err(), - "Serialization should fail for Timestamp with increment != 0" + "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 Timestamp with a non-zero increment to u32"), - "Expected error message to mention non-zero increment: {}", + err_string.contains("Cannot convert i32"), + "Expected error message to mention failed i32 to u32 conversion, got: {}", err_string ); #[serde_as] - #[derive(Deserialize, Serialize, Debug)] - struct A { - #[serde_as(as = "u32::AsTimestamp")] - pub time: u32, + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct E { + #[serde_as(as = "u32::AsI64")] + value: u32, - #[serde_as(as = "Option")] - pub time_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub time_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub time_vector: Vec, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let time = 12345; - let a = A { - time, - time_optional_none: None, - time_optional_some: Some(time), - time_vector: vec![time], + let value = u32::MAX; + let e = E { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], }; // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); + let doc = serialize_to_document(&e).unwrap(); // Validate serialized data assert_eq!( - doc.get_timestamp("time").unwrap(), - Timestamp { time, increment: 0 }, - "Expected serialized time to match the original." + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." ); assert_eq!( - doc.get("time_optional_none"), + doc.get("value_optional_none"), Some(&Bson::Null), - "Expected serialized time_optional_none to be None." - ); - - match doc.get("time_optional_some") { - Some(Bson::Timestamp(ts)) => { - assert_eq!( - *ts, - Timestamp { time, increment: 0 }, - "Expected serialized time_optional_some to match original time." - ) - } - _ => panic!("Expected serialized time_optional_some to be a BSON Timestamp."), - } - - let time_vector = doc - .get_array("time_vector") - .expect("Expected serialized time_vector to be a BSON array."); - let expected_time_vector: Vec = a - .time_vector - .iter() - .map(|val| { - Bson::Timestamp(Timestamp { - time: *val, - increment: 0, - }) - }) - .collect(); - assert_eq!( - time_vector, &expected_time_vector, - "Expected each serialized element in time_vector to match the original." - ); - - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - - // Validate deserialized data - assert_eq!( - a_deserialized.time, time, - "Expected deserialized time to match the original." + "Expected serialized value_optional_none to be None." ); assert_eq!( - a_deserialized.time_optional_none, None, - "Expected deserialized time_optional_none to be None." + doc.get("value_optional_some"), + Some(&Bson::Int64(value as i64)), + "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::Int64(value as i64)]; assert_eq!( - a_deserialized.time_optional_some, - Some(time), - "Expected deserialized time_optional_some to match the original." + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." ); + // Validate deserialized data + let e_deserialized: E = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.time_vector, - vec![time], - "Expected deserialized time_vector to match the original." + e_deserialized, e, + "Round-trip failed: deserialized struct did not match original." ); - // Validate deserializing error case with an invalid Timestamp - let invalid_timestamp_for_deserializing = Timestamp { - time: 0, - increment: 2, - }; - 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] + // Validate deserialization fails for i64::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i64::MIN; + let bad_e = 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(invalid_doc); + let result: Result = deserialize_from_document(bad_e); assert!( result.is_err(), - "Deserialization should fail for Timestamp with increment != 0" + "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("Cannot convert Timestamp with a non-zero increment to u32"), - "Expected error message to mention non-zero increment: {}", + err_string.contains("Cannot convert i64"), + "Expected error message to mention failed i64 to u32 conversion, got: {}", err_string ); } } #[test] -fn test_f64_helpers() { +fn test_u64_helpers() { + let _guard = LOCK.run_concurrently(); + #[cfg(feature = "serde_with-3")] { #[serde_as] @@ -2213,22 +1998,22 @@ fn test_f64_helpers() { ); #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, PartialEq, Debug)] struct B { - #[serde_as(as = "u32::AsF64")] - pub value: u32, + #[serde_as(as = "u64::AsI32")] + value: u64, - #[serde_as(as = "Option")] - pub value_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - pub value_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - pub value_vector: Vec, + #[serde_as(as = "Vec")] + value_vector: Vec, } - let value = 12345; + let value = 1; let b = B { value, value_optional_none: None, @@ -2241,9 +2026,9 @@ fn test_f64_helpers() { // Validate serialized data assert_eq!( - doc.get("value"), - Some(&Bson::Double(value as f64)), - "Expected serialized value to match the original." + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." ); assert_eq!( @@ -2254,44 +2039,249 @@ fn test_f64_helpers() { assert_eq!( doc.get("value_optional_some"), - Some(&Bson::Double(value as f64)), + Some(&Bson::Int32(value as i32)), "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)]; - + let expected_value_vector: Vec = vec![Bson::Int32(value as i32)]; assert_eq!( value_vector, &expected_value_vector, "Expected each serialized element in value_vector to match the original." ); - // Deserialize the BSON back to the struct + // Validate deserialized data let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "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_b: B = B { + 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_b); + 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_b = 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_b); + 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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct C { + #[serde_as(as = "u64::AsI64")] + value: u64, + + #[serde_as(as = "Option")] + value_optional_none: Option, + + #[serde_as(as = "Option")] + value_optional_some: Option, + + #[serde_as(as = "Vec")] + value_vector: Vec, + } + + let value = i64::MAX as u64; + let c = C { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." + ); + + 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::Int64(value as i64)), + "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::Int64(value as i64)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.value, value, - "Expected deserialized value to match the original." + c_deserialized, c, + "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_c: C = C { + 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_c); + 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_c = 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_c); + 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 ); + } +} + +#[test] +fn test_uuid_1_helpers() { + use serde_helpers::uuid_1; + use uuid::Uuid; + + let _guard = LOCK.run_concurrently(); + + #[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] + { + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "uuid_1::AsBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let a: A = A { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + match doc.get("uuid").unwrap() { + Bson::Binary(bin) => { + assert_eq!(bin.subtype, BinarySubtype::Uuid); + assert_eq!(bin.bytes, uuid.as_bytes()); + } + _ => panic!("Expected serialized uuid to match original as Bson::Binary"), + } assert_eq!( - b_deserialized.value_optional_none, None, - "Expected deserialized val_optional_none to be None." + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." ); + match doc.get("uuid_optional_some") { + Some(Bson::Binary(bin)) => { + assert_eq!(bin.subtype, BinarySubtype::Uuid); + assert_eq!(bin.bytes, uuid.as_bytes()); + } + _ => panic!("Expected serialized uuid_optional_some to be Bson::Binary."), + } + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected serialized uuid_vector to be a BSON array."); + let expected_uuid_vector: Vec = vec![Bson::Binary(Binary { + subtype: BinarySubtype::Uuid, + bytes: uuid.as_bytes().to_vec(), + })]; assert_eq!( - b_deserialized.value_optional_some, - Some(value), - "Expected deserialized val_optional_some to match the original." + uuid_vector, &expected_uuid_vector, + "Expected each serialized element in uuid_vector to match the original." ); + // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.value_vector, - vec![value], - "Expected deserialized val_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); } } From 1db7343ca6893d854396df143f0d5ab338e2b153 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 11:22:23 -0400 Subject: [PATCH 13/61] Refactor uuid to legacy binary converters using serde_conv --- src/serde_helpers.rs | 218 ++++++++++++------------ src/tests/serde.rs | 392 +++++++++++++++++++++++++++++++------------ 2 files changed, 395 insertions(+), 215 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 7d834017..cf21d773 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -8,25 +8,6 @@ use std::{ use serde::{de::Visitor, Deserialize, Serialize, Serializer}; -#[cfg(feature = "uuid-1")] -#[doc(inline)] -pub use uuid_1_as_c_sharp_legacy_binary::{ - deserialize as deserialize_uuid_1_from_c_sharp_legacy_binary, - serialize as serialize_uuid_1_as_c_sharp_legacy_binary, -}; -#[cfg(feature = "uuid-1")] -#[doc(inline)] -pub use uuid_1_as_java_legacy_binary::{ - deserialize as deserialize_uuid_1_from_java_legacy_binary, - serialize as serialize_uuid_1_as_java_legacy_binary, -}; -#[cfg(feature = "uuid-1")] -#[doc(inline)] -pub use uuid_1_as_python_legacy_binary::{ - deserialize as deserialize_uuid_1_from_python_legacy_binary, - serialize as serialize_uuid_1_as_python_legacy_binary, -}; - #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -537,11 +518,11 @@ pub mod uuid_1 { /// use serde::{Serialize, Deserialize}; /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; - /// # use serde_with::serde_as; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { - /// #[serde(with = "uuid_1::AsBinary")] + /// #[serde_as(as = "uuid_1::AsBinary")] /// pub id: Uuid, /// } /// # } @@ -555,6 +536,117 @@ pub mod uuid_1 { Ok(bson_uuid.into()) } ); + + serde_conv_doc!( + /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy C# driver + /// UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy C# driver + /// format. + /// + /// ```rust + /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] + /// # { + /// use serde::{Serialize, Deserialize}; + /// use uuid::Uuid; + /// use bson::serde_helpers::uuid_1; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "uuid_1::AsCSharpLegacyBinary")] + /// pub id: Uuid, + /// } + /// # } + /// ``` + pub AsCSharpLegacyBinary, + Uuid, + |uuid: &Uuid| -> Result { + let inner = crate::uuid::Uuid::from(*uuid); + Ok(crate::Binary::from_uuid_with_representation( + inner, + crate::uuid::UuidRepresentation::CSharpLegacy, + )) + }, + |binary: crate::Binary| -> Result { + let inner = binary + .to_uuid_with_representation(crate::uuid::UuidRepresentation::CSharpLegacy) + .map_err(|e| e.to_string())?; + Ok(inner.into()) + } + ); + + serde_conv_doc!( + /// /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy + /// Java driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy + /// Java driver format. + /// + /// ```rust + /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] + /// # { + /// use serde::{Serialize, Deserialize}; + /// use uuid::Uuid; + /// use bson::serde_helpers::uuid_1; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "uuid_1::AsJavaLegacyBinary")] + /// pub id: Uuid, + /// } + /// # } + /// ``` + pub AsJavaLegacyBinary, + Uuid, + |uuid: &Uuid| -> Result { + let inner = crate::uuid::Uuid::from(*uuid); + Ok(crate::Binary::from_uuid_with_representation( + inner, + crate::uuid::UuidRepresentation::JavaLegacy, + )) + }, + |binary: crate::Binary| -> Result { + let inner = binary + .to_uuid_with_representation(crate::uuid::UuidRepresentation::JavaLegacy) + .map_err(|e| e.to_string())?; + Ok(inner.into()) + } + ); + + serde_conv_doc!( + /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy Python + /// driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy Python + /// driver format. + /// + /// ```rust + /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] + /// # { + /// use serde::{Serialize, Deserialize}; + /// use uuid::Uuid; + /// use bson::serde_helpers::uuid_1; + /// # use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "uuid_1::AsPythonLegacyBinary")] + /// pub id: Uuid, + /// } + /// # } + /// ``` + pub AsPythonLegacyBinary, + Uuid, + |uuid: &Uuid| -> Result { + let inner = crate::uuid::Uuid::from(*uuid); + Ok(crate::Binary::from_uuid_with_representation( + inner, + crate::uuid::UuidRepresentation::PythonLegacy, + )) + }, + |binary: crate::Binary| -> Result { + let inner = binary + .to_uuid_with_representation(crate::uuid::UuidRepresentation::PythonLegacy) + .map_err(|e| e.to_string())?; + Ok(inner.into()) + } + ); } #[allow(unused_macros)] @@ -612,90 +704,6 @@ macro_rules! as_legacy_binary_mod { }; } -/// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy -/// Java driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy -/// Java driver format. -/// -/// ```rust -/// #[cfg(feature = "uuid-1")] -/// # { -/// use serde::{Serialize, Deserialize}; -/// use uuid::Uuid; -/// use bson::serde_helpers::uuid_1_as_java_legacy_binary; -/// -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "uuid_1_as_java_legacy_binary")] -/// pub id: Uuid, -/// } -/// # } -/// ``` -#[cfg(feature = "uuid-1")] -#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] -pub mod uuid_1_as_java_legacy_binary { - as_legacy_binary_mod!( - cfg(feature = "uuid-1"), - uuid::Uuid, - UuidRepresentation::JavaLegacy - ); -} - -/// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy Python -/// driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy Python -/// driver format. -/// -/// ```rust -/// # #[cfg(feature = "uuid-1")] -/// # { -/// use serde::{Serialize, Deserialize}; -/// use uuid::Uuid; -/// use bson::serde_helpers::uuid_1_as_python_legacy_binary; -/// -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "uuid_1_as_python_legacy_binary")] -/// pub id: Uuid, -/// } -/// # } -/// ``` -#[cfg(feature = "uuid-1")] -#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] -pub mod uuid_1_as_python_legacy_binary { - as_legacy_binary_mod!( - cfg(feature = "uuid-1"), - uuid::Uuid, - UuidRepresentation::PythonLegacy - ); -} - -/// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy C# driver -/// UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy C# driver -/// format. -/// -/// ```rust -/// # #[cfg(feature = "uuid-1")] -/// # { -/// use serde::{Serialize, Deserialize}; -/// use uuid::Uuid; -/// use bson::serde_helpers::uuid_1_as_c_sharp_legacy_binary; -/// -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "uuid_1_as_c_sharp_legacy_binary")] -/// pub id: Uuid, -/// } -/// # } -/// ``` -#[cfg(feature = "uuid-1")] -#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] -pub mod uuid_1_as_c_sharp_legacy_binary { - as_legacy_binary_mod!( - cfg(feature = "uuid-1"), - uuid::Uuid, - UuidRepresentation::CSharpLegacy - ); -} - /// 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 e5d0436b..46fdf42a 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, bson_datetime, object_id, u32, u64}, + serde_helpers::{bson_datetime, object_id, u32, u64, uuid_1}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -542,54 +542,6 @@ fn test_de_db_pointer() { assert_eq!(foo.db_pointer, db_pointer.clone()); } -#[cfg(feature = "uuid-1")] -#[test] -fn test_serde_legacy_uuid_1() { - use uuid::Uuid; - - let _guard = LOCK.run_concurrently(); - - #[derive(Serialize, Deserialize)] - struct Foo { - #[serde(with = "serde_helpers::uuid_1_as_java_legacy_binary")] - java_legacy: Uuid, - #[serde(with = "serde_helpers::uuid_1_as_python_legacy_binary")] - python_legacy: Uuid, - #[serde(with = "serde_helpers::uuid_1_as_c_sharp_legacy_binary")] - csharp_legacy: Uuid, - } - let uuid = Uuid::parse_str("00112233445566778899AABBCCDDEEFF").unwrap(); - let foo = Foo { - java_legacy: uuid, - python_legacy: uuid, - csharp_legacy: uuid, - }; - - let x = serialize_to_bson(&foo).unwrap(); - assert_eq!( - x.as_document().unwrap(), - &doc! { - "java_legacy": Bson::Binary(Binary{ - subtype:BinarySubtype::UuidOld, - bytes: hex::decode("7766554433221100FFEEDDCCBBAA9988").unwrap(), - }), - "python_legacy": Bson::Binary(Binary{ - subtype:BinarySubtype::UuidOld, - bytes: hex::decode("00112233445566778899AABBCCDDEEFF").unwrap(), - }), - "csharp_legacy": Bson::Binary(Binary{ - subtype:BinarySubtype::UuidOld, - bytes: hex::decode("33221100554477668899AABBCCDDEEFF").unwrap(), - }) - } - ); - - let foo: Foo = deserialize_from_bson(x).unwrap(); - assert_eq!(foo.java_legacy, uuid); - assert_eq!(foo.python_legacy, uuid); - assert_eq!(foo.csharp_legacy, uuid); -} - #[test] fn test_de_uuid_extjson_string() { let _guard = LOCK.run_concurrently(); @@ -2206,84 +2158,304 @@ fn test_u64_helpers() { } } +#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] #[test] fn test_uuid_1_helpers() { - use serde_helpers::uuid_1; + use crate::uuid::UuidRepresentation; use uuid::Uuid; let _guard = LOCK.run_concurrently(); - #[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] - { - #[serde_as] - #[derive(Serialize, Deserialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "uuid_1::AsBinary")] - uuid: Uuid, + fn assert_binary_match( + actual: &Bson, + uuid: &Uuid, + expected_subtype: BinarySubtype, + uuid_representation: UuidRepresentation, + ) { + match actual { + Bson::Binary(Binary { subtype, bytes }) => { + assert_eq!( + subtype, &expected_subtype, + "Expected subtype {:?}, but got {:?}", + expected_subtype, subtype + ); + let expected_bytes = { + let uuid: crate::Uuid = crate::uuid::Uuid::from(*uuid); + crate::Binary::from_uuid_with_representation(uuid, uuid_representation).bytes + }; + assert_eq!( + bytes, &expected_bytes, + "Serialized binary bytes did not match for representation {:?}", + uuid_representation + ); + } + other => panic!("Expected Bson::Binary, got {:?}", other), + } + } - #[serde_as(as = "Option")] - uuid_optional_none: Option, + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "uuid_1::AsBinary")] + uuid: Uuid, - #[serde_as(as = "Option")] - uuid_optional_some: Option, + #[serde_as(as = "Option")] + uuid_optional_none: Option, - #[serde_as(as = "Vec")] - uuid_vector: Vec, - } + #[serde_as(as = "Option")] + uuid_optional_some: Option, - let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let a: A = A { - uuid, - uuid_optional_none: None, - uuid_optional_some: Some(uuid), - uuid_vector: vec![uuid], - }; + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let a: A = A { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; - // Validate serialized data - match doc.get("uuid").unwrap() { - Bson::Binary(bin) => { - assert_eq!(bin.subtype, BinarySubtype::Uuid); - assert_eq!(bin.bytes, uuid.as_bytes()); - } - _ => panic!("Expected serialized uuid to match original as Bson::Binary"), - } + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - assert_eq!( - doc.get("uuid_optional_none"), - Some(&Bson::Null), - "Expected serialized uuid_optional_none to be None." - ); + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::Uuid, + UuidRepresentation::Standard, + ); - match doc.get("uuid_optional_some") { - Some(Bson::Binary(bin)) => { - assert_eq!(bin.subtype, BinarySubtype::Uuid); - assert_eq!(bin.bytes, uuid.as_bytes()); - } - _ => panic!("Expected serialized uuid_optional_some to be Bson::Binary."), - } + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); - let uuid_vector = doc - .get_array("uuid_vector") - .expect("Expected serialized uuid_vector to be a BSON array."); - let expected_uuid_vector: Vec = vec![Bson::Binary(Binary { - subtype: BinarySubtype::Uuid, - bytes: uuid.as_bytes().to_vec(), - })]; - assert_eq!( - uuid_vector, &expected_uuid_vector, - "Expected each serialized element in uuid_vector to match the original." - ); + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::Uuid, + UuidRepresentation::Standard, + ); - // Validate deserialized data - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - assert_eq!( - a_deserialized, a, - "Deserialized struct does not match original." - ); + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected serialized uuid_vector to be a BSON array."); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::Uuid, + UuidRepresentation::Standard, + ); + + // 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] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "uuid_1::AsCSharpLegacyBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let b: B = B { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::CSharpLegacy, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::CSharpLegacy, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected uuid_vector to be a BSON array"); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::CSharpLegacy, + ); + + // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "Deserialized struct does not match original." + ); + + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct C { + #[serde_as(as = "uuid_1::AsJavaLegacyBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let c: C = C { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::JavaLegacy, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::JavaLegacy, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected uuid_vector to be a BSON array"); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::JavaLegacy, + ); + + // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); + assert_eq!( + c_deserialized, c, + "Deserialized struct does not match original." + ); + + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct D { + #[serde_as(as = "uuid_1::AsPythonLegacyBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let d: D = D { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&d).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::PythonLegacy, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::PythonLegacy, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected uuid_vector to be a BSON array"); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::PythonLegacy, + ); + + // Validate deserialized data + let d_deserialized: D = deserialize_from_document(doc).unwrap(); + assert_eq!( + d_deserialized, d, + "Deserialized struct does not match original." + ); } #[test] From 4cdaf4b1a4b2639175f3b184b13b5858507c92f6 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 11:59:51 -0400 Subject: [PATCH 14/61] Shorten vector field assertions --- src/tests/serde.rs | 66 +++++++++------------------------------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 46fdf42a..b85aa34f 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -24,12 +24,11 @@ use crate::{ use serde::{Deserialize, Serialize}; use serde_json::json; use serde_with::serde_as; -use time::OffsetDateTime; - use std::{ collections::BTreeMap, convert::{TryFrom, TryInto}, }; +use time::OffsetDateTime; #[test] fn test_ser_vec() { @@ -665,11 +664,7 @@ fn test_oid_helpers() { let oid_vector = doc .get_array("oid_vector") .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = a - .oid_vector - .into_iter() - .map(|oid| Bson::ObjectId(ObjectId::parse_str(oid).unwrap())) - .collect(); + let expected_oid_vector: Vec = vec![Bson::ObjectId(oid)]; assert_eq!( oid_vector, &expected_oid_vector, "Expected each serialized element in oid_vector match the original." @@ -773,11 +768,7 @@ fn test_oid_helpers() { let oid_vector = doc .get_array("oid_vector") .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = b - .oid_vector - .into_iter() - .map(|oid| Bson::String(oid.to_hex())) - .collect(); + let expected_oid_vector: Vec = vec![Bson::String(oid.to_hex())]; assert_eq!( oid_vector, &expected_oid_vector, "Expected each serialized element in oid_vector match the original." @@ -890,11 +881,8 @@ fn test_bson_datetime_helpers() { let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = a - .date_vector - .iter() - .map(|dt| Bson::String(dt.try_to_rfc3339_string().unwrap())) - .collect(); + 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." @@ -998,11 +986,7 @@ fn test_bson_datetime_helpers() { let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = b - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(DateTime::from_millis(dt))) - .collect(); + 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." @@ -1088,11 +1072,7 @@ fn test_bson_datetime_helpers() { let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = c - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(DateTime::parse_rfc3339_str(dt).unwrap())) - .collect(); + 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." @@ -1205,11 +1185,7 @@ fn test_bson_datetime_helpers() { let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = a - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(dt.into())) - .collect(); + 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 \ @@ -1298,11 +1274,7 @@ fn test_bson_datetime_helpers() { let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = a - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(dt.into())) - .collect(); + 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 \ @@ -1394,11 +1366,7 @@ fn test_u32_helpers() { let timestamp_vector = doc .get_array("timestamp_vector") .expect("Expected serialized timestamp_vector to be a BSON array."); - let expected_timestamp_vector: Vec = a - .timestamp_vector - .iter() - .map(|ts| Bson::Int64(ts.time as i64)) - .collect(); + 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." @@ -1507,16 +1475,8 @@ fn test_u32_helpers() { let time_vector = doc .get_array("time_vector") .expect("Expected serialized time_vector to be a BSON array."); - let expected_time_vector: Vec = b - .time_vector - .iter() - .map(|val| { - Bson::Timestamp(Timestamp { - time: *val, - increment: 0, - }) - }) - .collect(); + 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." @@ -1621,7 +1581,6 @@ fn test_u32_helpers() { .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." @@ -1897,7 +1856,6 @@ fn test_u64_helpers() { .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)]; - // TODO: check whether this can be applied to other converters assert_eq!( value_vector, &expected_value_vector, "Expected each serialized element in value_vector to match the original." From 61e2b05806a84ad8bd0f919768868f93ab332385 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 12:00:06 -0400 Subject: [PATCH 15/61] Remove unused imports --- src/serde_helpers.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index cf21d773..49247fa0 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -78,7 +78,6 @@ pub mod bson_datetime { use chrono::Utc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Contains functions to serialize a [`crate::DateTime`] as an RFC 3339 (ISO 8601) formatted @@ -240,7 +239,6 @@ pub mod u32 { use crate::{macros::serde_conv_doc, Timestamp}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Contains functions to serialize a [`bson::Timestamp`] as a `u32` and deserialize a [`bson::Timestamp`] @@ -402,7 +400,6 @@ pub mod u64 { use crate::macros::serde_conv_doc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Contains functions to serialize a `u64` as an `f64` (BSON double) and deserialize a @@ -505,7 +502,6 @@ pub mod uuid_1 { use crate::macros::serde_conv_doc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; use uuid::Uuid; serde_conv_doc!( From b566c08aee733eba567c4cbb4c4b4a566562fb6b Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 13:09:13 -0400 Subject: [PATCH 16/61] Shorten deserialization assertions --- src/tests/serde.rs | 298 +++++++-------------------------------------- 1 file changed, 45 insertions(+), 253 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index b85aa34f..458e9301 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -610,7 +610,7 @@ fn test_oid_helpers() { #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, PartialEq, Debug)] struct A { #[serde_as(as = "object_id::FromHexString")] oid: String, @@ -670,26 +670,11 @@ fn test_oid_helpers() { "Expected each serialized element in oid_vector match the original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.oid, - oid.to_string(), - "Expected deserialized oid to match the original." - ); - - assert_eq!( - a_deserialized.oid_optional_some, - Some(oid.to_string()), - "Expected deserialized oid_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.oid_vector, - vec![oid.to_string()], - "Expected deserialized oid_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate serializing error case with an invalid ObjectId string @@ -713,7 +698,7 @@ fn test_oid_helpers() { ); #[serde_as] - #[derive(Serialize, Deserialize, Debug)] + #[derive(Serialize, Deserialize, Debug, PartialEq)] struct B { #[serde_as(as = "object_id::AsHexString")] oid: ObjectId, @@ -774,30 +759,11 @@ fn test_oid_helpers() { "Expected each serialized element in oid_vector match the original." ); - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.oid, oid, - "Expected deserialized oid to match the original." - ); - - assert_eq!( - b_deserialized.oid_optional_none, None, - "Expected deserialized oid_optional_none to be None." - ); - - assert_eq!( - b_deserialized.oid_optional_some, - Some(oid), - "Expected deserialized oid_optional_some to match the original." - ); - - assert_eq!( - b_deserialized.oid_vector, - vec![oid], - "Expected deserialized oid_vector to match the original.." + b_deserialized, b, + "Deserialized struct does not match original." ); // Validate deserializing error case with an invalid ObjectId string @@ -828,7 +794,7 @@ fn test_bson_datetime_helpers() { #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "bson_datetime::AsRfc3339String")] pub date: DateTime, @@ -888,30 +854,11 @@ fn test_bson_datetime_helpers() { "Expected each serialized element in date_vector to match the original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, date, - "Expected deserialized date to match the original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date], - "Expected deserialized date_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate deserializing error case with an invalid DateTime string @@ -934,7 +881,7 @@ fn test_bson_datetime_helpers() { ); #[serde_as] - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug, PartialEq)] struct B { #[serde_as(as = "bson_datetime::FromI64")] date: i64, @@ -992,35 +939,15 @@ fn test_bson_datetime_helpers() { "Expected each serialized element in date_vector match the original." ); - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.date, - date.timestamp_millis(), - "Expected deserialized date to match original." - ); - - assert_eq!( - b_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - b_deserialized.date_optional_some, - Some(date.timestamp_millis()), - "Expected deserialized date_optional_some to match original." - ); - - assert_eq!( - b_deserialized.date_vector, - vec![date.timestamp_millis()], - "Expected deserialized date_vector to match original." + b_deserialized, b, + "Deserialized struct does not match original." ); #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct C { #[serde_as(as = "bson_datetime::FromRfc3339String")] pub date: String, @@ -1078,31 +1005,11 @@ fn test_bson_datetime_helpers() { "Expected each serialized element in date_vector match the original." ); - // Deserialize the BSON back to the struct - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized.date, - date.try_to_rfc3339_string().unwrap(), - "Expected deserialized date to match original." - ); - - assert_eq!( - c_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - c_deserialized.date_optional_some, - Some(date.try_to_rfc3339_string().unwrap()), - "Expected deserialized date_optional_some to match original." - ); - - assert_eq!( - c_deserialized.date_vector, - vec![date.try_to_rfc3339_string().unwrap()], - "Expected deserialized date_vector to match original." + c_deserialized, c, + "Deserialized struct does not match original." ); // Validate serializing error case with an invalid DateTime string @@ -1131,7 +1038,7 @@ fn test_bson_datetime_helpers() { use std::str::FromStr; #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "bson_datetime::FromChronoDateTime")] pub date: chrono::DateTime, @@ -1192,37 +1099,18 @@ fn test_bson_datetime_helpers() { original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, date, - "Expected deserialized date to match original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date], - "Expected deserialized date_vector to match original." + a_deserialized, a, + "Deserialized struct does not match original." ); } #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] { #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "bson_datetime::FromTime03OffsetDateTime")] pub date: OffsetDateTime, @@ -1281,31 +1169,11 @@ fn test_bson_datetime_helpers() { original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, - date.to_time_0_3(), - "Expected deserialized date to match original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date.to_time_0_3()), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date.to_time_0_3()], - "Expected deserialized date_vector to match original." + a_deserialized, a, + "Deserialized struct does not match original." ); } } @@ -1317,7 +1185,7 @@ fn test_u32_helpers() { #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "u32::FromTimestamp")] pub timestamp: Timestamp, @@ -1372,30 +1240,11 @@ fn test_u32_helpers() { "Expected each serialized element in timestamp_vector to match the original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.timestamp, timestamp, - "Expected deserialized timestamp to match the original." - ); - - assert_eq!( - a_deserialized.timestamp_optional_none, None, - "Expected deserialized timestamp_optional_none to be None." - ); - - assert_eq!( - a_deserialized.timestamp_optional_some, - Some(timestamp), - "Expected deserialized timestamp_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.timestamp_vector, - vec![timestamp], - "Expected deserialized timestamp_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate serializing error case with an invalid Timestamp @@ -1422,7 +1271,7 @@ fn test_u32_helpers() { ); #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct B { #[serde_as(as = "u32::AsTimestamp")] pub time: u32, @@ -1482,30 +1331,11 @@ fn test_u32_helpers() { "Expected each serialized element in time_vector to match the original." ); - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.time, time, - "Expected deserialized time to match the original." - ); - - assert_eq!( - b_deserialized.time_optional_none, None, - "Expected deserialized time_optional_none to be None." - ); - - assert_eq!( - b_deserialized.time_optional_some, - Some(time), - "Expected deserialized time_optional_some to match the original." - ); - - assert_eq!( - b_deserialized.time_vector, - vec![time], - "Expected deserialized time_vector to match the original." + b_deserialized, b, + "Deserialized struct does not match original." ); // Validate deserializing error case with an invalid Timestamp @@ -1532,7 +1362,7 @@ fn test_u32_helpers() { ); #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct C { #[serde_as(as = "u32::AsF64")] pub value: u32, @@ -1586,30 +1416,11 @@ fn test_u32_helpers() { "Expected each serialized element in value_vector to match the original." ); - // Deserialize the BSON back to the struct - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized.value, value, - "Expected deserialized value to match the original." - ); - - assert_eq!( - c_deserialized.value_optional_none, None, - "Expected deserialized val_optional_none to be None." - ); - - assert_eq!( - c_deserialized.value_optional_some, - Some(value), - "Expected deserialized val_optional_some to match the original." - ); - - assert_eq!( - c_deserialized.value_vector, - vec![value], - "Expected deserialized val_vector to match the original." + c_deserialized, c, + "Deserialized struct does not match original." ); #[serde_as] @@ -1671,7 +1482,7 @@ fn test_u32_helpers() { let d_deserialized: D = deserialize_from_document(doc).unwrap(); assert_eq!( d_deserialized, d, - "Round-trip failed: deserialized struct did not match original." + "Deserialized struct does not match original." ); // Validate serialization fails because u32::MAX is too large to fit in i32 @@ -1807,7 +1618,7 @@ fn test_u64_helpers() { #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "u64::AsF64")] pub value: u64, @@ -1861,30 +1672,11 @@ fn test_u64_helpers() { "Expected each serialized element in value_vector to match the original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.value, value, - "Expected deserialized value to match the original." - ); - - assert_eq!( - a_deserialized.value_optional_none, None, - "Expected deserialized val_optional_none to be None." - ); - - assert_eq!( - a_deserialized.value_optional_some, - Some(value), - "Expected deserialized val_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.value_vector, - vec![value], - "Expected deserialized val_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate serializing error case with u64 over size limit From 181bcd2dbb0d861859ed862b5f5284ae8fa45665 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 14:43:56 -0400 Subject: [PATCH 17/61] Add documentation for modules --- README.md | 4 +- src/datetime.rs | 10 +- src/lib.rs | 6 +- src/serde_helpers.rs | 262 +++++++++++++++++++++++-------------------- src/tests/serde.rs | 44 ++++---- 5 files changed, 171 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index e9d98592..c4549dfe 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ provides a `DateTime` type, but its `Serialize` and `Deserialize` implementation instead, so when using it with BSON, the BSON datetime type is not used. To work around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of convenient conversions between `bson::DateTime` and `chrono::DateTime`, including the -[`bson_datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/bson_datetime/struct.FromChronoDateTime/index.html) +[`datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/datetime/struct.FromChronoDateTime/index.html) serde helper, which can be used to (de)serialize `chrono::DateTime`s to/from BSON datetimes, and the `From` implementation for `Bson`, which allows `chrono::DateTime` values to be used in the `doc!` and `bson!` macros. @@ -250,7 +250,7 @@ struct Foo { // serializes as a BSON datetime. // this requires the "chrono-0_4" feature flag - #[serde_as(as = "bson::serde_helpers::bson_datetime::FromChronoDateTime")] + #[serde_as(as = "bson::serde_helpers::datetime::FromChronoDateTime")] chrono_as_bson: chrono::DateTime, } diff --git a/src/datetime.rs b/src/datetime.rs index 81e46a3d..ccecefea 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -110,9 +110,9 @@ use crate::error::{Error, Result}; /// The `bson` crate provides a number of useful helpers for serializing and deserializing /// 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::bson_datetime::FromChronoDateTime`]. +/// [`crate::serde_helpers::datetime::FromChronoDateTime`]. /// Similarly, to serialize a BSON [`DateTime`] to a string, you can use -/// [`crate::serde_helpers::bson_datetime::AsRfc3339String`]. Check out the +/// [`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. /// @@ -121,7 +121,7 @@ use crate::error::{Error, Result}; /// # { /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; -/// use bson::serde_helpers::bson_datetime; +/// use bson::serde_helpers::datetime; /// /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -134,11 +134,11 @@ use crate::error::{Error, Result}; /// /// // serializes as a BSON datetime. /// // this requires the "chrono-0_4" feature flag -/// #[serde_as(as = "bson_datetime::FromChronoDateTime")] +/// #[serde_as(as = "datetime::FromChronoDateTime")] /// chrono_as_bson: chrono::DateTime, /// /// // serializes as an RFC 3339 / ISO-8601 string. -/// #[serde_as(as = "bson_datetime::AsRfc3339String")] +/// #[serde_as(as = "datetime::AsRfc3339String")] /// bson_as_string: bson::DateTime, /// } /// # } diff --git a/src/lib.rs b/src/lib.rs index a4972cc4..bbb1785f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ //! on strings instead, so when using it with BSON, the BSON datetime type is not used. To work //! around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of //! convenient conversions between [`bson::DateTime`](crate::DateTime) and [`chrono::DateTime`], -//! including the [`serde_helpers::bson_datetime::FromChronoDateTime`] +//! including the [`serde_helpers::datetime::FromChronoDateTime`] //! serde helper, which can be used to (de)serialize [`chrono::DateTime`]s to/from BSON datetimes, //! and the `From` implementation for [`Bson`], which allows [`chrono::DateTime`] //! values to be used in the `doc!` and `bson!` macros. @@ -245,7 +245,7 @@ //! use serde::{Serialize, Deserialize}; //! use serde_with::serde_as; //! use bson::doc; -//! use bson::serde_helpers::bson_datetime; +//! use bson::serde_helpers::datetime; //! //! #[serde_as] //! #[derive(Serialize, Deserialize)] @@ -258,7 +258,7 @@ //! //! // serializes as a BSON datetime. //! // this requires the "chrono-0_4" feature flag -//! #[serde_as(as = "bson_datetime::FromChronoDateTime")] +//! #[serde_as(as = "datetime::FromChronoDateTime")] //! chrono_as_bson: chrono::DateTime, //! } //! diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 49247fa0..1ba01bf3 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,13 +1,19 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde +use crate::{oid::ObjectId, Binary, DateTime, Timestamp}; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; use std::{ marker::PhantomData, ops::{Deref, DerefMut}, result::Result, }; +use uuid::Uuid; -use serde::{de::Visitor, Deserialize, Serialize, Serializer}; - +/// Type converters for serializing and deserializing [`ObjectId`] using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`object_id::AsHexString`] — serializes an [`ObjectId`] as a hex string. +/// - [`object_id::FromHexString`] — serializes a hex string as an [`ObjectId`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -16,15 +22,13 @@ pub mod object_id { use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// Contains functions to serialize an ObjectId as a hex string and deserialize an - /// ObjectId from a hex string + /// Serializes an [`ObjectId`] as a hex string and deserializes an [`ObjectId`] from a hex string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::object_id; - /// # use serde_with::serde_as; - /// # use bson::oid::ObjectId; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::object_id, oid::ObjectId}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -44,14 +48,13 @@ pub mod object_id { ); serde_conv_doc!( - /// Contains functions to serialize a hex string as an ObjectId and deserialize a - /// hex string from an ObjectId + /// Serializes a hex string as an [`ObjectId`] and deserializes a hex string from an [`ObjectId`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::object_id; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::object_id; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -71,30 +74,37 @@ pub mod object_id { ); } +/// Type converters for serializing and deserializing [`DateTime`] using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`datetime::AsRfc3339String`] — serializes a [`DateTime`] as a RFC 3339 string. +/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`DateTime`]. +/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`DateTime`]. +/// - [`datetime::FromI64`] — serializes a `i64` as a [`DateTime`]. +/// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a +/// [`DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod bson_datetime { +pub mod datetime { use crate::{macros::serde_conv_doc, DateTime}; use chrono::Utc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// Contains functions to serialize a [`crate::DateTime`] as an RFC 3339 (ISO 8601) formatted - /// string and deserialize a [`crate::DateTime`] from an RFC 3339 (ISO 8601) formatted - /// string. - /// + /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes + /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::datetime, DateTime}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::AsRfc3339String")] - /// pub date: bson::DateTime, + /// #[serde_as(as = "datetime::AsRfc3339String")] + /// pub date: DateTime, /// } /// # } /// ``` @@ -111,20 +121,18 @@ pub mod bson_datetime { ); serde_conv_doc!( - /// 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`]. - /// + /// Serializes an RFC 3339 (ISO 8601) formatted string as a [`DateTime`] and deserializes an + /// RFC 3339 (ISO 8601) formatted string from a [`DateTime`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::datetime; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromRfc3339String")] + /// #[serde_as(as = "datetime::FromRfc3339String")] /// pub date: String, /// } /// # } @@ -142,24 +150,20 @@ pub mod bson_datetime { ); serde_conv_doc!( - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))) - )] - /// Contains functions to serialize a [`chrono::DateTime`] as a [`crate::DateTime`] and - /// deserialize a [`chrono::DateTime`] from a [`crate::DateTime`]. - /// + #[cfg(feature = "chrono-0_4")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] + /// Serializes a [`chrono::DateTime`] as a [`DateTime`] and deserializes a [`chrono::DateTime`] + /// from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::datetime; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromChronoDateTime")] + /// #[serde_as(as = "datetime::FromChronoDateTime")] /// pub date: chrono::DateTime, /// } /// # } @@ -175,21 +179,19 @@ pub mod bson_datetime { ); serde_conv_doc!( - /// Contains functions to serialize a `i64` integer as [`crate::DateTime`] and - /// deserialize a `i64` integer from [`crate::DateTime`]. - /// - /// ### The i64 should represent seconds `(DateTime::timestamp_millis(..))`. + /// Serializes a `i64` integer as [`DateTime`] and deserializes a `i64` integer from [`DateTime`]. /// + /// The `i64` should represent seconds `(DateTime::timestamp_millis(..))`. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::datetime; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { - /// #[serde_as(as = "bson_datetime::FromI64")] + /// #[serde_as(as = "datetime::FromI64")] /// pub now: i64, /// } /// # } @@ -205,19 +207,18 @@ pub mod bson_datetime { ); serde_conv_doc!( - /// Contains functions to serialize a [`time::OffsetDateTime`] as a [`crate::DateTime`] and - /// deserialize a [`time::OffsetDateTime`] from a [`crate::DateTime`]. - /// + /// Serializes a [`time::OffsetDateTime`] as a [`DateTime`] and deserializes a + /// [`time::OffsetDateTime`] from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::datetime; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromTime03OffsetDatetTime")] + /// #[serde_as(as = "datetime::FromTime03OffsetDatetTime")] /// pub date: time::OffsetDateTime, /// } /// # } @@ -233,6 +234,14 @@ pub mod bson_datetime { ); } +/// Type converters for serializing and deserializing `u32` using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`u32::FromTimestamp`] — serializes a [`Timestamp`] as a `u32`. +/// - [`u32::AsTimestamp`] — serializes a `u32` as a [`Timestamp`]. +/// - [`u32::AsF64`] — serializes a `u32` as a `f64`. +/// - [`u32::AsI32`] — serializes a `u32` as a `i32`. +/// - [`u32::AsI64`] — serializes a `u32` as a `i64`. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod u32 { @@ -241,16 +250,17 @@ pub mod u32 { use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// 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. + /// Serializes a [`Timestamp`] as a `u32` and deserializes a [`Timestamp`] 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 serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32, Timestamp}; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::u32, Timestamp}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -273,15 +283,17 @@ pub mod u32 { ); serde_conv_doc!( - /// 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. + /// Serializes a `u32` as a [`Timestamp`] and deserializes a `u32` 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 serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u32; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::u32; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { @@ -304,17 +316,15 @@ pub mod u32 { ); serde_conv_doc!( - /// Contains functions to serialize a `u32` as an `f64` (BSON double) and deserialize a - /// `u32` from an `f64` (BSON double). - /// - /// Deserialization errors if an exact conversion is not possible. + /// Serializes a `u32` as an `f64` and deserializes a `u32` from an `f64`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u32; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::u32; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct FileInfo { @@ -332,21 +342,21 @@ pub mod u32 { if (value - value as u32 as f64).abs() <= f64::EPSILON { Ok(value as u32) } else { - Err(format!("Cannot convert f64 (BSON double) {} to u32", value)) + Err(format!("Cannot convert f64 {} to u32", value)) } } ); serde_conv_doc!( - /// Contains functions to serialize a `u32` as a `i32` and deserialize a `u32` - /// from a `i32`. Errors if an exact conversion is not possible. + /// Serializes a `u32` as an `i32` and deserializes a `u32` from an `i32`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32}; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::u32}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -366,15 +376,15 @@ pub mod u32 { ); serde_conv_doc!( - /// Contains functions to serialize a `u32` as a `i64` and deserialize a `u32` - /// from a `i64`. Errors if an exact conversion is not possible. + /// Serializes a `u32` as an `i64` and deserializes a `u32` from an `i64`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32}; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::u32}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -394,6 +404,12 @@ pub mod u32 { ); } +/// Type converters for serializing and deserializing `u64` using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`u64::AsF64`] — serializes a `u64` as a `f64`. +/// - [`u64::AsI32`] — serializes a `u64` as a `i32`. +/// - [`u64::AsI64`] — serializes a `u64` as a `i64`. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod u64 { @@ -402,17 +418,15 @@ pub mod u64 { use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// Contains functions to serialize a `u64` as an `f64` (BSON double) and deserialize a - /// `u64` from an `f64` (BSON double). + /// Serializes a `u64` as an `f64` and deserializes a `u64` from an `f64`. /// /// Deserialization errors if an exact conversion is not possible. - /// /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u64; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::serde_helpers::u64; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct FileInfo { @@ -427,28 +441,28 @@ pub mod u64 { if value < &u64::MAX && *value == *value as f64 as u64 { Ok(*value as f64) } else { - Err(format!("Cannot convert u64 {} to f64 (BSON double)", value)) + 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 (BSON double) {} to u64", value)) + Err(format!("Cannot convert f64 {} to u64", value)) } } ); serde_conv_doc!( - /// Contains functions to serialize a `u64` as a `i32` and deserialize a `u64` - /// from a `i32`. Errors if an exact conversion is not possible. + /// Serializes a `u64` as an `i32` and deserializes a `u64` from an `i32`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u64}; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::u64}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -468,15 +482,15 @@ pub mod u64 { ); serde_conv_doc!( - /// Contains functions to serialize a `u64` as a `i64` and deserialize a `u64` - /// from a `i64`. Errors if an exact conversion is not possible. + /// Serializes a `u64` as an `i64` and deserializes a `u64` from an `i64`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u64}; - /// # use serde_with::serde_as; + /// use serde::{Serialize, Deserialize}; + /// use bson::{serde_helpers::u64}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -496,18 +510,26 @@ pub mod u64 { ); } +/// Type converters for serializing and deserializing [`Uuid`] using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`uuid_1::AsBinary`] — serializes a [`Uuid`] as a [`Binary`]. +/// - [`uuid_1::AsCSharpLegacyBinary`] — serializes a [`Uuid`] as a [`Binary`] in the legacy C# +/// driver UUID format. +/// - [`uuid_1::AsJavaLegacyBinary`] — serializes a [`Uuid`] as a [`Binary`] in the legacy Java +/// driver UUID format. +/// - [`uuid_1::AsPythonLegacyBinary`] — serializes a [`Uuid`] as a [`Binary`] in the legacy Python +/// driver UUID format. #[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "serde_with-3", feature = "uuid-1"))))] pub mod uuid_1 { - use crate::macros::serde_conv_doc; + use crate::{macros::serde_conv_doc, Binary}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; use uuid::Uuid; serde_conv_doc!( - /// Contains functions to serialize a [`uuid::Uuid`] as a [`crate::Binary`] and deserialize a - /// [`uuid::Uuid`] from a [`crate::Binary`]. - /// + /// Serializes a [`Uuid`] as a [`Binary`] and deserializes a [`Uuid`] from a [`Binary`]. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { @@ -534,10 +556,8 @@ pub mod uuid_1 { ); serde_conv_doc!( - /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy C# driver - /// UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy C# driver - /// format. - /// + /// Serializes a [`Uuid`] to a [`Binary`] in the legacy C# driver UUID format and + /// deserializes [`Uuid`] from a [`Binary`] in the legacy C# driver format. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { @@ -571,17 +591,15 @@ pub mod uuid_1 { ); serde_conv_doc!( - /// /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy - /// Java driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy - /// Java driver format. - /// + /// Serializes a [`Uuid`] to a [`Binary`] in the legacy Java driver UUID format and + /// deserializes [`Uuid`] from a [`Binary`] in the legacy Java driver format. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { /// use serde::{Serialize, Deserialize}; /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; - /// # use serde_with::serde_as; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -608,17 +626,15 @@ pub mod uuid_1 { ); serde_conv_doc!( - /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy Python - /// driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy Python - /// driver format. - /// + /// Serializes a [`Uuid`] to a [`Binary`] in the legacy Python driver UUID format and + /// deserializes [`Uuid`] from a [`Binary`] in the legacy Python driver format. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { /// use serde::{Serialize, Deserialize}; /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; - /// # use serde_with::serde_as; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 458e9301..c9c145f4 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::{bson_datetime, object_id, u32, u64, uuid_1}, + serde_helpers::{datetime, object_id, u32, u64, uuid_1}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -788,7 +788,7 @@ fn test_oid_helpers() { } #[test] -fn test_bson_datetime_helpers() { +fn test_datetime_helpers() { let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] @@ -796,16 +796,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "bson_datetime::AsRfc3339String")] + #[serde_as(as = "datetime::AsRfc3339String")] pub date: DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } @@ -883,16 +883,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Serialize, Deserialize, Debug, PartialEq)] struct B { - #[serde_as(as = "bson_datetime::FromI64")] + #[serde_as(as = "datetime::FromI64")] date: i64, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] date_vector: Vec, } @@ -949,16 +949,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct C { - #[serde_as(as = "bson_datetime::FromRfc3339String")] + #[serde_as(as = "datetime::FromRfc3339String")] pub date: String, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } @@ -1040,16 +1040,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "bson_datetime::FromChronoDateTime")] + #[serde_as(as = "datetime::FromChronoDateTime")] pub date: chrono::DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option>, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option>, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec>, } @@ -1112,16 +1112,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "bson_datetime::FromTime03OffsetDateTime")] + #[serde_as(as = "datetime::FromTime03OffsetDateTime")] pub date: OffsetDateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } From ed526cb0c64336619e101247f39672aa860d016b Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 16:38:15 -0400 Subject: [PATCH 18/61] Reorder imports in documentation --- src/serde_helpers.rs | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 1ba01bf3..988492de 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -26,8 +26,8 @@ pub mod object_id { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::object_id, oid::ObjectId}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -52,8 +52,8 @@ pub mod object_id { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::object_id; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -97,8 +97,8 @@ pub mod datetime { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::datetime, DateTime}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -126,8 +126,8 @@ pub mod datetime { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -157,8 +157,8 @@ pub mod datetime { /// ```rust /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -185,8 +185,8 @@ pub mod datetime { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -212,8 +212,8 @@ pub mod datetime { /// ```rust /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -258,8 +258,8 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::u32, Timestamp}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -291,8 +291,8 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::u32; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -322,8 +322,8 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::u32; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -354,8 +354,8 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::u32}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -382,8 +382,8 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::u32}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -424,8 +424,8 @@ pub mod u64 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::serde_helpers::u64; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -460,8 +460,8 @@ pub mod u64 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::u64}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -488,8 +488,8 @@ pub mod u64 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// use serde::{Serialize, Deserialize}; /// use bson::{serde_helpers::u64}; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -533,10 +533,10 @@ pub mod uuid_1 { /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; + /// use uuid::Uuid; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -561,10 +561,10 @@ pub mod uuid_1 { /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; + /// use uuid::Uuid; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -596,10 +596,10 @@ pub mod uuid_1 { /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; + /// use uuid::Uuid; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -631,10 +631,10 @@ pub mod uuid_1 { /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; /// use bson::serde_helpers::uuid_1; + /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; + /// use uuid::Uuid; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { From a63f82e793b1289d9ac4bd28390222d80c3611c0 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Fri, 11 Jul 2025 16:51:21 -0400 Subject: [PATCH 19/61] Shorten *_optional_some field assertions --- src/tests/serde.rs | 119 +++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 79 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index c9c145f4..8459e11d 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -649,17 +649,11 @@ fn test_oid_helpers() { "Expected serialized oid_optional_none to be None." ); - match doc.get("oid_optional_some") { - Some(Bson::ObjectId(value)) => { - assert_eq!( - *value, oid, - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON ObjectId.") - } - } + assert_eq!( + doc.get("oid_optional_some"), + Some(&Bson::ObjectId(oid)), + "Expected serialized oid_optional_some to match original." + ); let oid_vector = doc .get_array("oid_vector") @@ -737,18 +731,11 @@ fn test_oid_helpers() { "Expected serialized oid_optional_none to be None." ); - match doc.get("oid_optional_some") { - Some(Bson::String(value)) => { - assert_eq!( - *value, - oid.to_hex(), - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON String.") - } - } + assert_eq!( + doc.get("oid_optional_some"), + Some(&Bson::String(oid.to_hex())), + "Expected serialized oid_optional_some to match original." + ); let oid_vector = doc .get_array("oid_vector") @@ -834,15 +821,11 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::String(value)) => { - assert_eq!( - value, iso, - "Expected serialized date_optional_some to match original date." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON String."), - } + 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") @@ -920,15 +903,11 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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") @@ -986,15 +965,11 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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") @@ -1078,16 +1053,11 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, - DateTime::from_chrono(date), - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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") @@ -1149,15 +1119,11 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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") @@ -1310,16 +1276,11 @@ fn test_u32_helpers() { "Expected serialized time_optional_none to be None." ); - match doc.get("time_optional_some") { - Some(Bson::Timestamp(ts)) => { - assert_eq!( - *ts, - Timestamp { time, increment: 0 }, - "Expected serialized time_optional_some to match original time." - ) - } - _ => panic!("Expected serialized time_optional_some to be a BSON Timestamp."), - } + 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") From 2298c43029de0a7a9fe640cd2a88cb9624b8c5d1 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 09:18:48 -0400 Subject: [PATCH 20/61] Remove bson from datetime helpers module name --- README.md | 4 ++-- src/datetime.rs | 10 +++++----- src/lib.rs | 6 +++--- src/serde_helpers.rs | 37 +++++++++++++------------------------ src/tests/serde.rs | 34 +++++++++++++++++----------------- 5 files changed, 40 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index e9d98592..c4549dfe 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ provides a `DateTime` type, but its `Serialize` and `Deserialize` implementation instead, so when using it with BSON, the BSON datetime type is not used. To work around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of convenient conversions between `bson::DateTime` and `chrono::DateTime`, including the -[`bson_datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/bson_datetime/struct.FromChronoDateTime/index.html) +[`datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/datetime/struct.FromChronoDateTime/index.html) serde helper, which can be used to (de)serialize `chrono::DateTime`s to/from BSON datetimes, and the `From` implementation for `Bson`, which allows `chrono::DateTime` values to be used in the `doc!` and `bson!` macros. @@ -250,7 +250,7 @@ struct Foo { // serializes as a BSON datetime. // this requires the "chrono-0_4" feature flag - #[serde_as(as = "bson::serde_helpers::bson_datetime::FromChronoDateTime")] + #[serde_as(as = "bson::serde_helpers::datetime::FromChronoDateTime")] chrono_as_bson: chrono::DateTime, } diff --git a/src/datetime.rs b/src/datetime.rs index 81e46a3d..58500f02 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -110,9 +110,9 @@ use crate::error::{Error, Result}; /// The `bson` crate provides a number of useful helpers for serializing and deserializing /// 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::bson_datetime::FromChronoDateTime`]. +/// [`crate::serde_helpers::datetimemChronoDateTime`]. /// Similarly, to serialize a BSON [`DateTime`] to a string, you can use -/// [`crate::serde_helpers::bson_datetime::AsRfc3339String`]. Check out the +/// [`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. /// @@ -121,7 +121,7 @@ use crate::error::{Error, Result}; /// # { /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; -/// use bson::serde_helpers::bson_datetime; +/// use bson::serde_helpers::datetime; /// /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -134,11 +134,11 @@ use crate::error::{Error, Result}; /// /// // serializes as a BSON datetime. /// // this requires the "chrono-0_4" feature flag -/// #[serde_as(as = "bson_datetime::FromChronoDateTime")] +/// #[serde_as(as = "datetime::FromChronoDateTime")] /// chrono_as_bson: chrono::DateTime, /// /// // serializes as an RFC 3339 / ISO-8601 string. -/// #[serde_as(as = "bson_datetime::AsRfc3339String")] +/// #[serde_as(as = "datetime::AsRfc3339String")] /// bson_as_string: bson::DateTime, /// } /// # } diff --git a/src/lib.rs b/src/lib.rs index a4972cc4..bbb1785f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ //! on strings instead, so when using it with BSON, the BSON datetime type is not used. To work //! around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of //! convenient conversions between [`bson::DateTime`](crate::DateTime) and [`chrono::DateTime`], -//! including the [`serde_helpers::bson_datetime::FromChronoDateTime`] +//! including the [`serde_helpers::datetime::FromChronoDateTime`] //! serde helper, which can be used to (de)serialize [`chrono::DateTime`]s to/from BSON datetimes, //! and the `From` implementation for [`Bson`], which allows [`chrono::DateTime`] //! values to be used in the `doc!` and `bson!` macros. @@ -245,7 +245,7 @@ //! use serde::{Serialize, Deserialize}; //! use serde_with::serde_as; //! use bson::doc; -//! use bson::serde_helpers::bson_datetime; +//! use bson::serde_helpers::datetime; //! //! #[serde_as] //! #[derive(Serialize, Deserialize)] @@ -258,7 +258,7 @@ //! //! // serializes as a BSON datetime. //! // this requires the "chrono-0_4" feature flag -//! #[serde_as(as = "bson_datetime::FromChronoDateTime")] +//! #[serde_as(as = "datetime::FromChronoDateTime")] //! chrono_as_bson: chrono::DateTime, //! } //! diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 8ddd1ae0..43680c8d 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -9,17 +9,6 @@ use std::{ use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; -#[doc(inline)] -pub use i64_as_bson_datetime::{ - deserialize as deserialize_i64_from_bson_datetime, - serialize as serialize_i64_as_bson_datetime, -}; -#[cfg(feature = "time-0_3")] -#[doc(inline)] -pub use time_0_3_offsetdatetime_as_bson_datetime::{ - deserialize as deserialize_time_0_3_offsetdatetime_from_bson_datetime, - serialize as serialize_time_0_3_offsetdatetime_as_bson_datetime, -}; #[doc(inline)] pub use timestamp_as_u32::{ deserialize as deserialize_timestamp_from_u32, @@ -239,17 +228,17 @@ pub mod u64_as_f64 { /// # #[cfg(feature = "time-0_3")] /// # { /// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::time_0_3_offsetdatetime_as_bson_datetime; +/// # use bson::serde_helpers::time_0_3_offsetdatetime_as_datetime; /// #[derive(Serialize, Deserialize)] /// struct Event { -/// #[serde(with = "time_0_3_offsetdatetime_as_bson_datetime")] +/// #[serde(with = "time_0_3_offsetdatetime_as_datetime")] /// pub date: time::OffsetDateTime, /// } /// # } /// ``` #[cfg(feature = "time-0_3")] #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] -pub mod time_0_3_offsetdatetime_as_bson_datetime { +pub mod time_0_3_offsetdatetime_as_datetime { use crate::DateTime; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::result::Result; @@ -277,7 +266,7 @@ pub mod time_0_3_offsetdatetime_as_bson_datetime { #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod bson_datetime { +pub mod datetime { use crate::{macros::serde_conv_doc, DateTime}; use chrono::Utc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -293,12 +282,12 @@ pub mod bson_datetime { /// # #[cfg(feature = "serde_with-3")] /// { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::AsRfc3339String")] + /// #[serde_as(as = "datetime::AsRfc3339String")] /// pub date: bson::DateTime, /// } /// # } @@ -324,12 +313,12 @@ pub mod bson_datetime { /// # #[cfg(feature = "serde_with-3")] /// { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromRfc3339String")] + /// #[serde_as(as = "datetime::FromRfc3339String")] /// pub date: String, /// } /// # } @@ -359,12 +348,12 @@ pub mod bson_datetime { /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromChronoDateTime")] + /// #[serde_as(as = "datetime::FromChronoDateTime")] /// pub date: chrono::DateTime, /// } /// # } @@ -387,14 +376,14 @@ pub mod bson_datetime { /// /// ```rust /// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::i64_as_bson_datetime; +/// # use bson::serde_helpers::i64_as_datetime; /// #[derive(Serialize, Deserialize)] /// struct Item { -/// #[serde(with = "i64_as_bson_datetime")] +/// #[serde(with = "i64_as_datetime")] /// pub now: i64, /// } /// ``` -pub mod i64_as_bson_datetime { +pub mod i64_as_datetime { use crate::DateTime; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/src/tests/serde.rs b/src/tests/serde.rs index b25456de..6604ad53 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -9,8 +9,8 @@ use crate::{ oid::ObjectId, serde_helpers::{ self, - bson_datetime, - i64_as_bson_datetime, + datetime, + i64_as_datetime, object_id, timestamp_as_u32, u32_as_timestamp, @@ -758,16 +758,16 @@ fn test_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug)] struct A { - #[serde_as(as = "bson_datetime::AsRfc3339String")] + #[serde_as(as = "datetime::AsRfc3339String")] pub date: DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } @@ -871,7 +871,7 @@ fn test_datetime_helpers() { #[derive(Deserialize, Serialize)] struct B { - #[serde(with = "serde_helpers::time_0_3_offsetdatetime_as_bson_datetime")] + #[serde(with = "serde_helpers::time_0_3_offsetdatetime_as_datetime")] pub date: time::OffsetDateTime, } @@ -900,16 +900,16 @@ fn test_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct A { - #[serde_as(as = "bson_datetime::FromChronoDateTime")] + #[serde_as(as = "datetime::FromChronoDateTime")] pub date: chrono::DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option>, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option>, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec>, } @@ -995,16 +995,16 @@ fn test_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct C { - #[serde_as(as = "bson_datetime::FromRfc3339String")] + #[serde_as(as = "datetime::FromRfc3339String")] pub date: String, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } @@ -1335,12 +1335,12 @@ fn test_oid_helpers() { } #[test] -fn test_i64_as_bson_datetime() { +fn test_i64_as_datetime() { let _guard = LOCK.run_concurrently(); #[derive(Serialize, Deserialize)] struct A { - #[serde(with = "i64_as_bson_datetime")] + #[serde(with = "i64_as_datetime")] now: i64, } From 7cdc3d9c3bda2402a54ff78e1e0f2204c63bb039 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 09:23:58 -0400 Subject: [PATCH 21/61] Add RFC 3339 format to error messages --- src/serde_helpers.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 43680c8d..5ea6a759 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -296,11 +296,11 @@ pub mod datetime { DateTime, |date: &DateTime| -> Result { date.try_to_rfc3339_string().map_err(|e| { - format!("Cannot format DateTime {} as String: {}", date, e) + format!("Cannot format DateTime {} as RFC 3339 string: {}", date, e) }) }, |string: String| -> Result { - DateTime::parse_rfc3339_str(&string).map_err(|e| format!("Cannot format String {} as DateTime: {}", string, e)) + DateTime::parse_rfc3339_str(&string).map_err(|e| format!("Cannot format RFC 3339 string {} as DateTime: {}", string, e)) } ); @@ -326,11 +326,11 @@ pub mod datetime { pub FromRfc3339String, String, |string: &String| -> Result { - DateTime::parse_rfc3339_str(string).map_err(|e| format!("Cannot format String {} as DateTime: {}", string, e)) + DateTime::parse_rfc3339_str(string).map_err(|e| format!("Cannot format RFC 3339 string {} as DateTime: {}", string, e)) }, |date: DateTime| -> Result { date.try_to_rfc3339_string().map_err(|e| { - format!("Cannot format DateTime {} as String: {}", date, e) + format!("Cannot format DateTime {} as RFC 3339 string: {}", date, e) }) } ); From 76349a0dc86556115eeec0ba39a644672ecb88d7 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 09:30:09 -0400 Subject: [PATCH 22/61] Fix typo --- src/datetime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datetime.rs b/src/datetime.rs index 58500f02..ccecefea 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -110,7 +110,7 @@ use crate::error::{Error, Result}; /// The `bson` crate provides a number of useful helpers for serializing and deserializing /// 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::datetimemChronoDateTime`]. +/// [`crate::serde_helpers::datetime::FromChronoDateTime`]. /// 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 From 4e4291eaf7db002ad0953f13098a53b19b2313a9 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 09:57:06 -0400 Subject: [PATCH 23/61] Fix typo 'DatetTime' --- src/serde_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 988492de..45662a96 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -218,7 +218,7 @@ pub mod datetime { /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "datetime::FromTime03OffsetDatetTime")] + /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] /// pub date: time::OffsetDateTime, /// } /// # } From e9b14b8307892611695216ce3136eac34f9194cb Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 10:18:00 -0400 Subject: [PATCH 24/61] Refactor DateTime FromI64 and FromTime03OffsetDateTime using serde_conv macro --- src/serde_helpers.rs | 128 ++++++-------- src/tests/serde.rs | 403 +++++++++++++++++++++---------------------- 2 files changed, 246 insertions(+), 285 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 5ea6a759..0a482ce2 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -221,49 +221,6 @@ pub mod u64_as_f64 { } } -/// Contains functions to serialize a [`time::OffsetDateTime`] as a [`crate::DateTime`] and -/// deserialize a [`time::OffsetDateTime`] from a [`crate::DateTime`]. -/// -/// ```rust -/// # #[cfg(feature = "time-0_3")] -/// # { -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::time_0_3_offsetdatetime_as_datetime; -/// #[derive(Serialize, Deserialize)] -/// struct Event { -/// #[serde(with = "time_0_3_offsetdatetime_as_datetime")] -/// pub date: time::OffsetDateTime, -/// } -/// # } -/// ``` -#[cfg(feature = "time-0_3")] -#[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] -pub mod time_0_3_offsetdatetime_as_datetime { - use crate::DateTime; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use std::result::Result; - - /// Deserializes a [`time::OffsetDateTime`] from a [`crate::DateTime`]. - #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let datetime = DateTime::deserialize(deserializer)?; - Ok(datetime.to_time_0_3()) - } - - /// Serializes a [`time::OffsetDateTime`] as a [`crate::DateTime`]. - #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] - pub fn serialize( - val: &time::OffsetDateTime, - serializer: S, - ) -> Result { - let datetime = DateTime::from_time_0_3(val.to_owned()); - datetime.serialize(serializer) - } -} - #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod datetime { @@ -367,40 +324,61 @@ pub mod datetime { Ok(bson_date.to_chrono()) } ); -} - -/// Contains functions to `serialize` a `i64` integer as [`crate::DateTime`] and -/// `deserialize` a `i64` integer from [`crate::DateTime`]. -/// -/// ### The i64 should represent seconds `(DateTime::timestamp_millis(..))`. -/// -/// ```rust -/// # use serde::{Serialize, Deserialize}; -/// # use bson::serde_helpers::i64_as_datetime; -/// #[derive(Serialize, Deserialize)] -/// struct Item { -/// #[serde(with = "i64_as_datetime")] -/// pub now: i64, -/// } -/// ``` -pub mod i64_as_datetime { - use crate::DateTime; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - /// Deserializes a i64 integer from a DateTime. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let date: DateTime = DateTime::deserialize(deserializer)?; - Ok(date.timestamp_millis()) - } + serde_conv_doc!( + /// Serializes a `i64` integer as [`DateTime`] and deserializes a `i64` integer from [`DateTime`]. + /// + /// The `i64` should represent seconds `(DateTime::timestamp_millis(..))`. + /// ```rust + /// # #[cfg(feature = "serde_with-3")] + /// # { + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Item { + /// #[serde_as(as = "datetime::FromI64")] + /// pub now: i64, + /// } + /// # } + /// ``` + pub FromI64, + i64, + |value: &i64| -> Result { + Ok(DateTime::from_millis(*value)) + }, + |date: DateTime| -> Result { + Ok(date.timestamp_millis()) + } + ); - /// Serializes a i64 integer as a DateTime. - pub fn serialize(val: &i64, serializer: S) -> Result { - let date_time = DateTime::from_millis(*val); - date_time.serialize(serializer) - } + serde_conv_doc!( + /// Serializes a [`time::OffsetDateTime`] as a [`DateTime`] and deserializes a + /// [`time::OffsetDateTime`] from a [`DateTime`]. + /// ```rust + /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] + /// # { + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Event { + /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] + /// pub date: time::OffsetDateTime, + /// } + /// # } + /// ``` + pub FromTime03OffsetDateTime, + time::OffsetDateTime, + |value: &time::OffsetDateTime| -> Result { + Ok(DateTime::from_time_0_3(*value)) + }, + |date: DateTime| -> Result { + Ok(date.to_time_0_3()) + } + ); } #[allow(unused_macros)] diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 6604ad53..f93e8f03 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -7,14 +7,7 @@ use crate::{ deserialize_from_document, doc, oid::ObjectId, - serde_helpers::{ - self, - datetime, - i64_as_datetime, - object_id, - timestamp_as_u32, - u32_as_timestamp, - }, + serde_helpers::{self, datetime, object_id, timestamp_as_u32, u32_as_timestamp}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -27,6 +20,7 @@ use crate::{ Serializer, Timestamp, }; +use time::OffsetDateTime; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -749,14 +743,12 @@ fn test_unsigned_helpers() { #[test] fn test_datetime_helpers() { - use time::{format_description::well_known::Rfc3339, OffsetDateTime}; - let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "datetime::AsRfc3339String")] pub date: DateTime, @@ -796,53 +788,27 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::String(value)) => { - assert_eq!( - value, iso, - "Expected serialized date_optional_some to match original date." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON String."), - } + 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 = a - .date_vector - .iter() - .map(|dt| Bson::String(dt.try_to_rfc3339_string().unwrap())) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, date, - "Expected deserialized date to match the original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date], - "Expected deserialized date_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate deserializing error case with an invalid DateTime string @@ -863,72 +829,38 @@ fn test_datetime_helpers() { "Expected error message to mention BSON error: {}", err_string ); - } - - #[cfg(feature = "time-0_3")] - { - use time::macros::datetime; - - #[derive(Deserialize, Serialize)] - struct B { - #[serde(with = "serde_helpers::time_0_3_offsetdatetime_as_datetime")] - pub date: time::OffsetDateTime, - } - - 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 = datetime!(2020-06-09 10:58:07.095 UTC); - assert_eq!(b.date, expected); - let doc = serialize_to_document(&b).unwrap(); - assert_eq!(doc.get_datetime("date").unwrap().to_time_0_3(), expected); - let b: B = deserialize_from_document(doc).unwrap(); - assert_eq!(b.date, expected); - } - - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - { - use std::str::FromStr; #[serde_as] - #[derive(Deserialize, Serialize)] - struct A { - #[serde_as(as = "datetime::FromChronoDateTime")] - pub date: chrono::DateTime, + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "datetime::FromI64")] + date: i64, - #[serde_as(as = "Option")] - pub date_optional_none: Option>, + #[serde_as(as = "Option")] + date_optional_none: Option, - #[serde_as(as = "Option")] - pub date_optional_some: Option>, + #[serde_as(as = "Option")] + date_optional_some: Option, - #[serde_as(as = "Vec")] - pub date_vector: Vec>, + #[serde_as(as = "Vec")] + 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, + let date = DateTime::now(); + let b = B { + date: date.timestamp_millis(), date_optional_none: None, - date_optional_some: Some(date), - date_vector: vec![date], + 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(); + let doc = serialize_to_document(&b).unwrap(); // Validate serialized data assert_eq!( - doc.get_datetime("date").unwrap().to_chrono(), - date, + doc.get_datetime("date").unwrap(), + &date, "Expected serialized date to match original date." ); @@ -938,62 +870,30 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, - DateTime::from_chrono(date), - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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 = a - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(dt.into())) - .collect(); + 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." + "Expected each serialized element in date_vector match the original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, date, - "Expected deserialized date to match original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date], - "Expected deserialized date_vector to match original." + b_deserialized, b, + "Deserialized struct does not match original." ); - } - #[cfg(feature = "serde_with-3")] - { #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct C { #[serde_as(as = "datetime::FromRfc3339String")] pub date: String, @@ -1008,20 +908,21 @@ fn test_datetime_helpers() { pub date_vector: Vec, } - let date = "2020-06-09T10:58:07.095Z"; + let date = DateTime::now(); let c = C { - date: date.to_string(), + date: date.try_to_rfc3339_string().unwrap(), date_optional_none: None, - date_optional_some: Some(date.to_string()), - date_vector: vec![date.to_string()], + 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(&c).unwrap(); // Validate serialized data - assert!( - doc.get_datetime("date").is_ok(), + assert_eq!( + *doc.get_datetime("date").unwrap(), + date, "Expected serialized date to be a BSON DateTime." ); @@ -1031,59 +932,26 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - let expected_date = DateTime::from_time_0_3( - OffsetDateTime::parse(date, &Rfc3339) - .expect("Failed to parse date string into DateTime."), + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(date)), + "Expected serialized date_optional_some to match original." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, expected_date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON String."), - } - let date_vector = doc .get_array("date_vector") .expect("Expected serialized date_vector to be a BSON array."); - let expected_date_vector: Vec = c - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(DateTime::parse_rfc3339_str(dt).unwrap())) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized.date, - date.to_string(), - "Expected deserialized date to match original." - ); - - assert_eq!( - c_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - c_deserialized.date_optional_some, - Some(date.to_string()), - "Expected deserialized date_optional_some to match original." - ); - - assert_eq!( - c_deserialized.date_vector, - vec![date.to_string()], - "Expected deserialized date_vector to match original." + c_deserialized, c, + "Deserialized struct does not match original." ); // Validate serializing error case with an invalid DateTime string @@ -1097,7 +965,7 @@ fn test_datetime_helpers() { let result = serialize_to_document(&bad_c); assert!( result.is_err(), - "Deserialization should fail for invalid DateTime strings" + "Serialization should fail for invalid DateTime strings" ); let err_string = format!("{:?}", result.unwrap_err()); assert!( @@ -1106,6 +974,141 @@ fn test_datetime_helpers() { err_string ); } + + #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] + { + use std::str::FromStr; + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromChronoDateTime")] + pub date: chrono::DateTime, + + #[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 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(); + + // 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_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." + ); + } + + #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] + { + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[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 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(); + + // 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] @@ -1334,26 +1337,6 @@ fn test_oid_helpers() { } } -#[test] -fn test_i64_as_datetime() { - let _guard = LOCK.run_concurrently(); - - #[derive(Serialize, Deserialize)] - struct A { - #[serde(with = "i64_as_datetime")] - now: i64, - } - - let now = DateTime::now(); - let a = A { - now: now.timestamp_millis(), - }; - let doc = serialize_to_document(&a).unwrap(); - assert_eq!(doc.get_datetime("now").unwrap(), &now); - let a: A = deserialize_from_document(doc).unwrap(); - assert_eq!(a.now, now.timestamp_millis()); -} - #[test] #[cfg(feature = "uuid-1")] fn test_uuid_1_helpers() { From 212ea0b45cb5e6a7f59420387ce928ff04e1c05c Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 10:34:09 -0400 Subject: [PATCH 25/61] Add rustdoc comments to module and structs --- src/serde_helpers.rs | 58 ++++++++++++++++++++++---------------------- src/tests/serde.rs | 3 ++- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 0a482ce2..0f39f6f1 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -221,6 +221,15 @@ pub mod u64_as_f64 { } } +/// Type converters for serializing and deserializing [`DateTime`] using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`datetime::AsRfc3339String`] — serializes a [`DateTime`] as a RFC 3339 string. +/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`DateTime`]. +/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`DateTime`]. +/// - [`datetime::FromI64`] — serializes a `i64` as a [`DateTime`]. +/// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a +/// [`DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod datetime { @@ -231,21 +240,19 @@ pub mod datetime { use std::result::Result; serde_conv_doc!( - /// Contains functions to serialize a [`crate::DateTime`] as an RFC 3339 (ISO 8601) formatted - /// string and deserialize a [`crate::DateTime`] from an RFC 3339 (ISO 8601) formatted - /// string. - /// + /// /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes + /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// # { + /// use bson::{serde_helpers::datetime, DateTime}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { /// #[serde_as(as = "datetime::AsRfc3339String")] - /// pub date: bson::DateTime, + /// pub date: DateTime, /// } /// # } /// ``` @@ -262,16 +269,14 @@ pub mod datetime { ); serde_conv_doc!( - /// 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`]. - /// + /// Serializes an RFC 3339 (ISO 8601) formatted string as a [`DateTime`] and deserializes an + /// RFC 3339 (ISO 8601) formatted string from a [`DateTime`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// # { + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { @@ -279,7 +284,6 @@ pub mod datetime { /// pub date: String, /// } /// # } - /// ``` pub FromRfc3339String, String, |string: &String| -> Result { @@ -293,20 +297,16 @@ pub mod datetime { ); serde_conv_doc!( - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))) - )] - /// Contains functions to serialize a [`chrono::DateTime`] as a [`crate::DateTime`] and - /// deserialize a [`chrono::DateTime`] from a [`crate::DateTime`]. - /// + #[cfg(feature = "chrono-0_4")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] + /// Serializes a [`chrono::DateTime`] as a [`DateTime`] and deserializes a [`chrono::DateTime`] + /// from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index f93e8f03..384665f2 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -20,7 +20,6 @@ use crate::{ Serializer, Timestamp, }; -use time::OffsetDateTime; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -743,6 +742,8 @@ fn test_unsigned_helpers() { #[test] fn test_datetime_helpers() { + use time::OffsetDateTime; + let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] From 03027d185c7edcf0e36406fd731f3b0098a0ffc8 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 11:12:41 -0400 Subject: [PATCH 26/61] Switch from clone through to_owned() to dereference --- src/serde_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 0f39f6f1..cef8a2f7 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -318,7 +318,7 @@ pub mod datetime { pub FromChronoDateTime, chrono::DateTime, |chrono_date: &chrono::DateTime| -> Result { - Ok(DateTime::from_chrono(chrono_date.to_owned())) + Ok(DateTime::from_chrono(*chrono_date)) }, |bson_date: DateTime| -> Result, String> { Ok(bson_date.to_chrono()) From 7077c0b64783baef45e55a429c467e976a567249 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 12:50:52 -0400 Subject: [PATCH 27/61] Rename bson_datetime module to datetime --- README.md | 4 ++-- src/datetime.rs | 10 +++++----- src/lib.rs | 6 +++--- src/serde_helpers.rs | 22 +++++++++++----------- src/tests/serde.rs | 44 ++++++++++++++++++++++---------------------- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index e9d98592..c4549dfe 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ provides a `DateTime` type, but its `Serialize` and `Deserialize` implementation instead, so when using it with BSON, the BSON datetime type is not used. To work around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of convenient conversions between `bson::DateTime` and `chrono::DateTime`, including the -[`bson_datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/bson_datetime/struct.FromChronoDateTime/index.html) +[`datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/datetime/struct.FromChronoDateTime/index.html) serde helper, which can be used to (de)serialize `chrono::DateTime`s to/from BSON datetimes, and the `From` implementation for `Bson`, which allows `chrono::DateTime` values to be used in the `doc!` and `bson!` macros. @@ -250,7 +250,7 @@ struct Foo { // serializes as a BSON datetime. // this requires the "chrono-0_4" feature flag - #[serde_as(as = "bson::serde_helpers::bson_datetime::FromChronoDateTime")] + #[serde_as(as = "bson::serde_helpers::datetime::FromChronoDateTime")] chrono_as_bson: chrono::DateTime, } diff --git a/src/datetime.rs b/src/datetime.rs index 81e46a3d..58500f02 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -110,9 +110,9 @@ use crate::error::{Error, Result}; /// The `bson` crate provides a number of useful helpers for serializing and deserializing /// 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::bson_datetime::FromChronoDateTime`]. +/// [`crate::serde_helpers::datetimemChronoDateTime`]. /// Similarly, to serialize a BSON [`DateTime`] to a string, you can use -/// [`crate::serde_helpers::bson_datetime::AsRfc3339String`]. Check out the +/// [`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. /// @@ -121,7 +121,7 @@ use crate::error::{Error, Result}; /// # { /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; -/// use bson::serde_helpers::bson_datetime; +/// use bson::serde_helpers::datetime; /// /// #[serde_as] /// #[derive(Serialize, Deserialize)] @@ -134,11 +134,11 @@ use crate::error::{Error, Result}; /// /// // serializes as a BSON datetime. /// // this requires the "chrono-0_4" feature flag -/// #[serde_as(as = "bson_datetime::FromChronoDateTime")] +/// #[serde_as(as = "datetime::FromChronoDateTime")] /// chrono_as_bson: chrono::DateTime, /// /// // serializes as an RFC 3339 / ISO-8601 string. -/// #[serde_as(as = "bson_datetime::AsRfc3339String")] +/// #[serde_as(as = "datetime::AsRfc3339String")] /// bson_as_string: bson::DateTime, /// } /// # } diff --git a/src/lib.rs b/src/lib.rs index a4972cc4..bbb1785f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ //! on strings instead, so when using it with BSON, the BSON datetime type is not used. To work //! around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of //! convenient conversions between [`bson::DateTime`](crate::DateTime) and [`chrono::DateTime`], -//! including the [`serde_helpers::bson_datetime::FromChronoDateTime`] +//! including the [`serde_helpers::datetime::FromChronoDateTime`] //! serde helper, which can be used to (de)serialize [`chrono::DateTime`]s to/from BSON datetimes, //! and the `From` implementation for [`Bson`], which allows [`chrono::DateTime`] //! values to be used in the `doc!` and `bson!` macros. @@ -245,7 +245,7 @@ //! use serde::{Serialize, Deserialize}; //! use serde_with::serde_as; //! use bson::doc; -//! use bson::serde_helpers::bson_datetime; +//! use bson::serde_helpers::datetime; //! //! #[serde_as] //! #[derive(Serialize, Deserialize)] @@ -258,7 +258,7 @@ //! //! // serializes as a BSON datetime. //! // this requires the "chrono-0_4" feature flag -//! #[serde_as(as = "bson_datetime::FromChronoDateTime")] +//! #[serde_as(as = "datetime::FromChronoDateTime")] //! chrono_as_bson: chrono::DateTime, //! } //! diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index cf21d773..071292bd 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -73,7 +73,7 @@ pub mod object_id { #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] -pub mod bson_datetime { +pub mod datetime { use crate::{macros::serde_conv_doc, DateTime}; use chrono::Utc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -89,12 +89,12 @@ pub mod bson_datetime { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::AsRfc3339String")] + /// #[serde_as(as = "datetime::AsRfc3339String")] /// pub date: bson::DateTime, /// } /// # } @@ -120,12 +120,12 @@ pub mod bson_datetime { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromRfc3339String")] + /// #[serde_as(as = "datetime::FromRfc3339String")] /// pub date: String, /// } /// # } @@ -155,12 +155,12 @@ pub mod bson_datetime { /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromChronoDateTime")] + /// #[serde_as(as = "datetime::FromChronoDateTime")] /// pub date: chrono::DateTime, /// } /// # } @@ -185,12 +185,12 @@ pub mod bson_datetime { /// # #[cfg(feature = "serde_with-3")] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { - /// #[serde_as(as = "bson_datetime::FromI64")] + /// #[serde_as(as = "datetime::FromI64")] /// pub now: i64, /// } /// # } @@ -213,12 +213,12 @@ pub mod bson_datetime { /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] /// # { /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::bson_datetime; + /// # use bson::serde_helpers::datetime; /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "bson_datetime::FromTime03OffsetDatetTime")] + /// #[serde_as(as = "datetime::FromTime03OffsetDatetTime")] /// pub date: time::OffsetDateTime, /// } /// # } diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 46fdf42a..2751a0f6 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::{bson_datetime, object_id, u32, u64, uuid_1}, + serde_helpers::{datetime, object_id, u32, u64, uuid_1}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -831,7 +831,7 @@ fn test_oid_helpers() { } #[test] -fn test_bson_datetime_helpers() { +fn test_datetime_helpers() { let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] @@ -839,16 +839,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug)] struct A { - #[serde_as(as = "bson_datetime::AsRfc3339String")] + #[serde_as(as = "datetime::AsRfc3339String")] pub date: DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } @@ -948,16 +948,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Serialize, Deserialize)] struct B { - #[serde_as(as = "bson_datetime::FromI64")] + #[serde_as(as = "datetime::FromI64")] date: i64, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] date_vector: Vec, } @@ -1038,16 +1038,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct C { - #[serde_as(as = "bson_datetime::FromRfc3339String")] + #[serde_as(as = "datetime::FromRfc3339String")] pub date: String, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } @@ -1153,16 +1153,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct A { - #[serde_as(as = "bson_datetime::FromChronoDateTime")] + #[serde_as(as = "datetime::FromChronoDateTime")] pub date: chrono::DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option>, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option>, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec>, } @@ -1248,16 +1248,16 @@ fn test_bson_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize)] struct A { - #[serde_as(as = "bson_datetime::FromTime03OffsetDateTime")] + #[serde_as(as = "datetime::FromTime03OffsetDateTime")] pub date: OffsetDateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } From ebe3a990e47eacb1b82e16064a95c639dc898db5 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 14:20:00 -0400 Subject: [PATCH 28/61] Remove extra rustdocs /// --- src/serde_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index cef8a2f7..ff237c62 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -240,7 +240,7 @@ pub mod datetime { use std::result::Result; serde_conv_doc!( - /// /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes + /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] From 9f41ee6a5d486af173817365207e183ce9f5901d Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 14:37:03 -0400 Subject: [PATCH 29/61] Add DateTime import to fix lint on rustdocs --- src/serde_helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index ff237c62..a734471d 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,5 +1,7 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde +use crate::DateTime; +use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; use std::{ convert::TryFrom, marker::PhantomData, @@ -7,8 +9,6 @@ use std::{ result::Result, }; -use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; - #[doc(inline)] pub use timestamp_as_u32::{ deserialize as deserialize_timestamp_from_u32, From e6b69c5444fe7166a835104396645de83227bcb9 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 14:58:53 -0400 Subject: [PATCH 30/61] Merge branch 'RUST-1748-serde_conv-2' into RUST-1748-serde_conv-3 --- src/datetime.rs | 2 +- src/serde_helpers.rs | 91 ++++++++-------- src/tests/serde.rs | 240 +++++++++---------------------------------- 3 files changed, 96 insertions(+), 237 deletions(-) diff --git a/src/datetime.rs b/src/datetime.rs index 58500f02..ccecefea 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -110,7 +110,7 @@ use crate::error::{Error, Result}; /// The `bson` crate provides a number of useful helpers for serializing and deserializing /// 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::datetimemChronoDateTime`]. +/// [`crate::serde_helpers::datetime::FromChronoDateTime`]. /// 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 diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 071292bd..b6ec625f 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,13 +1,13 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde +use crate::DateTime; +use serde::{de::Visitor, Deserialize, Serialize, Serializer}; use std::{ marker::PhantomData, ops::{Deref, DerefMut}, result::Result, }; -use serde::{de::Visitor, Deserialize, Serialize, Serializer}; - #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -71,6 +71,15 @@ pub mod object_id { ); } +/// Type converters for serializing and deserializing [`DateTime`] using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`datetime::AsRfc3339String`] — serializes a [`DateTime`] as a RFC 3339 string. +/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`DateTime`]. +/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`DateTime`]. +/// - [`datetime::FromI64`] — serializes a `i64` as a [`DateTime`]. +/// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a +/// [`DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod datetime { @@ -81,21 +90,19 @@ pub mod datetime { use std::result::Result; serde_conv_doc!( - /// Contains functions to serialize a [`crate::DateTime`] as an RFC 3339 (ISO 8601) formatted - /// string and deserialize a [`crate::DateTime`] from an RFC 3339 (ISO 8601) formatted - /// string. - /// + /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes + /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// use bson::{serde_helpers::datetime, DateTime}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { /// #[serde_as(as = "datetime::AsRfc3339String")] - /// pub date: bson::DateTime, + /// pub date: DateTime, /// } /// # } /// ``` @@ -103,25 +110,23 @@ pub mod datetime { DateTime, |date: &DateTime| -> Result { date.try_to_rfc3339_string().map_err(|e| { - format!("Cannot format DateTime {} as String: {}", date, e) + format!("Cannot format DateTime {} as RFC 3339 string: {}", date, e) }) }, |string: String| -> Result { - DateTime::parse_rfc3339_str(&string).map_err(|e| format!("Cannot format String {} as DateTime: {}", string, e)) + DateTime::parse_rfc3339_str(&string).map_err(|e| format!("Cannot format RFC 3339 string {} as DateTime: {}", string, e)) } ); serde_conv_doc!( - /// 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`]. - /// + /// Serializes an RFC 3339 (ISO 8601) formatted string as a [`DateTime`] and deserializes an + /// RFC 3339 (ISO 8601) formatted string from a [`DateTime`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { @@ -129,34 +134,29 @@ pub mod datetime { /// pub date: String, /// } /// # } - /// ``` pub FromRfc3339String, String, |string: &String| -> Result { - DateTime::parse_rfc3339_str(string).map_err(|e| format!("Cannot format String {} as DateTime: {}", string, e)) + DateTime::parse_rfc3339_str(string).map_err(|e| format!("Cannot format RFC 3339 string {} as DateTime: {}", string, e)) }, |date: DateTime| -> Result { date.try_to_rfc3339_string().map_err(|e| { - format!("Cannot format DateTime {} as String: {}", date, e) + format!("Cannot format DateTime {} as RFC 3339 string: {}", date, e) }) } ); serde_conv_doc!( - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))) - )] - /// Contains functions to serialize a [`chrono::DateTime`] as a [`crate::DateTime`] and - /// deserialize a [`chrono::DateTime`] from a [`crate::DateTime`]. - /// + #[cfg(feature = "chrono-0_4")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] + /// Serializes a [`chrono::DateTime`] as a [`DateTime`] and deserializes a [`chrono::DateTime`] + /// from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { @@ -176,17 +176,15 @@ pub mod datetime { ); serde_conv_doc!( - /// Contains functions to serialize a `i64` integer as [`crate::DateTime`] and - /// deserialize a `i64` integer from [`crate::DateTime`]. - /// - /// ### The i64 should represent seconds `(DateTime::timestamp_millis(..))`. + /// Serializes a `i64` integer as [`DateTime`] and deserializes a `i64` integer from [`DateTime`]. /// + /// The `i64` should represent seconds `(DateTime::timestamp_millis(..))`. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -206,19 +204,18 @@ pub mod datetime { ); serde_conv_doc!( - /// Contains functions to serialize a [`time::OffsetDateTime`] as a [`crate::DateTime`] and - /// deserialize a [`time::OffsetDateTime`] from a [`crate::DateTime`]. - /// + /// Serializes a [`time::OffsetDateTime`] as a [`DateTime`] and deserializes a + /// [`time::OffsetDateTime`] from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::datetime; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::datetime; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "datetime::FromTime03OffsetDatetTime")] + /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] /// pub date: time::OffsetDateTime, /// } /// # } diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 2751a0f6..d2f11e31 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -837,7 +837,7 @@ fn test_datetime_helpers() { #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "datetime::AsRfc3339String")] pub date: DateTime, @@ -877,53 +877,27 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::String(value)) => { - assert_eq!( - value, iso, - "Expected serialized date_optional_some to match original date." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON String."), - } + 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 = a - .date_vector - .iter() - .map(|dt| Bson::String(dt.try_to_rfc3339_string().unwrap())) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, date, - "Expected deserialized date to match the original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date], - "Expected deserialized date_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate deserializing error case with an invalid DateTime string @@ -946,7 +920,7 @@ fn test_datetime_helpers() { ); #[serde_as] - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug, PartialEq)] struct B { #[serde_as(as = "datetime::FromI64")] date: i64, @@ -985,58 +959,30 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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 = b - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(DateTime::from_millis(dt))) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); assert_eq!( - b_deserialized.date, - date.timestamp_millis(), - "Expected deserialized date to match original." - ); - - assert_eq!( - b_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - b_deserialized.date_optional_some, - Some(date.timestamp_millis()), - "Expected deserialized date_optional_some to match original." - ); - - assert_eq!( - b_deserialized.date_vector, - vec![date.timestamp_millis()], - "Expected deserialized date_vector to match original." + b_deserialized, b, + "Deserialized struct does not match original." ); #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct C { #[serde_as(as = "datetime::FromRfc3339String")] pub date: String, @@ -1075,54 +1021,26 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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 = c - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(DateTime::parse_rfc3339_str(dt).unwrap())) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized.date, - date.try_to_rfc3339_string().unwrap(), - "Expected deserialized date to match original." - ); - - assert_eq!( - c_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - c_deserialized.date_optional_some, - Some(date.try_to_rfc3339_string().unwrap()), - "Expected deserialized date_optional_some to match original." - ); - - assert_eq!( - c_deserialized.date_vector, - vec![date.try_to_rfc3339_string().unwrap()], - "Expected deserialized date_vector to match original." + c_deserialized, c, + "Deserialized struct does not match original." ); // Validate serializing error case with an invalid DateTime string @@ -1151,7 +1069,7 @@ fn test_datetime_helpers() { use std::str::FromStr; #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "datetime::FromChronoDateTime")] pub date: chrono::DateTime, @@ -1191,62 +1109,34 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, - DateTime::from_chrono(date), - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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 = a - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(dt.into())) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, date, - "Expected deserialized date to match original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date], - "Expected deserialized date_vector to match original." + a_deserialized, a, + "Deserialized struct does not match original." ); } #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] { #[serde_as] - #[derive(Deserialize, Serialize)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "datetime::FromTime03OffsetDateTime")] pub date: OffsetDateTime, @@ -1285,55 +1175,27 @@ fn test_datetime_helpers() { "Expected serialized date_optional_none to be None." ); - match doc.get("date_optional_some") { - Some(Bson::DateTime(value)) => { - assert_eq!( - *value, date, - "Expected serialized date_optional_some to match original." - ) - } - _ => panic!("Expected serialized date_optional_some to be a BSON DateTime."), - } + 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 = a - .date_vector - .into_iter() - .map(|dt| Bson::DateTime(dt.into())) - .collect(); + 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." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.date, - date.to_time_0_3(), - "Expected deserialized date to match original." - ); - - assert_eq!( - a_deserialized.date_optional_none, None, - "Expected deserialized date_optional_none to be None." - ); - - assert_eq!( - a_deserialized.date_optional_some, - Some(date.to_time_0_3()), - "Expected deserialized date_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.date_vector, - vec![date.to_time_0_3()], - "Expected deserialized date_vector to match original." + a_deserialized, a, + "Deserialized struct does not match original." ); } } From 2d26b59924080f2d6282e63c0d7a098faf8c5bd2 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 15:08:49 -0400 Subject: [PATCH 31/61] Remove DateTime import for rustdocs to avoid clippy warning --- src/serde_helpers.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index a734471d..0d2ab43d 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,6 +1,4 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde - -use crate::DateTime; use serde::{de::Visitor, ser, Deserialize, Serialize, Serializer}; use std::{ convert::TryFrom, @@ -221,15 +219,16 @@ pub mod u64_as_f64 { } } -/// Type converters for serializing and deserializing [`DateTime`] using [`serde_with::serde_as`]. +/// Type converters for serializing and deserializing [`crate::DateTime`] using +/// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`datetime::AsRfc3339String`] — serializes a [`DateTime`] as a RFC 3339 string. -/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`DateTime`]. -/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`DateTime`]. -/// - [`datetime::FromI64`] — serializes a `i64` as a [`DateTime`]. +/// - [`datetime::AsRfc3339String`] — serializes a [`crate::DateTime`] as a RFC 3339 string. +/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`crate::DateTime`]. +/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`crate::DateTime`]. +/// - [`datetime::FromI64`] — serializes a `i64` as a [`crate::DateTime`]. /// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a -/// [`DateTime`]. +/// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod datetime { From d3d9a95de173485d533262d9dc36741b64f7acbf Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 15:13:42 -0400 Subject: [PATCH 32/61] Add missing conditional compilation to OffsetDateTime struct --- src/serde_helpers.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 0d2ab43d..63d2cf32 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -353,6 +353,8 @@ pub mod datetime { ); serde_conv_doc!( + #[cfg(feature = "time-0_3")] + #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] /// Serializes a [`time::OffsetDateTime`] as a [`DateTime`] and deserializes a /// [`time::OffsetDateTime`] from a [`DateTime`]. /// ```rust From e1e4daeed2929ac79a1e27803b476e9b8fe0f18f Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 15:51:36 -0400 Subject: [PATCH 33/61] Merge branch 2 into branch 3 --- src/serde_helpers.rs | 29 ++++++++++++++--------------- src/tests/serde.rs | 29 ++++++----------------------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index b6ec625f..a3aa2845 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,6 +1,4 @@ //! Collection of helper functions for serializing to and deserializing from BSON using Serde - -use crate::DateTime; use serde::{de::Visitor, Deserialize, Serialize, Serializer}; use std::{ marker::PhantomData, @@ -71,15 +69,16 @@ pub mod object_id { ); } -/// Type converters for serializing and deserializing [`DateTime`] using [`serde_with::serde_as`]. +/// Type converters for serializing and deserializing [`crate::DateTime`] using +/// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`datetime::AsRfc3339String`] — serializes a [`DateTime`] as a RFC 3339 string. -/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`DateTime`]. -/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`DateTime`]. -/// - [`datetime::FromI64`] — serializes a `i64` as a [`DateTime`]. +/// - [`datetime::AsRfc3339String`] — serializes a [`crate::DateTime`] as a RFC 3339 string. +/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`crate::DateTime`]. +/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`crate::DateTime`]. +/// - [`datetime::FromI64`] — serializes a `i64` as a [`crate::DateTime`]. /// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a -/// [`DateTime`]. +/// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod datetime { @@ -204,6 +203,8 @@ pub mod datetime { ); serde_conv_doc!( + #[cfg(feature = "time-0_3")] + #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] /// Serializes a [`time::OffsetDateTime`] as a [`DateTime`] and deserializes a /// [`time::OffsetDateTime`] from a [`DateTime`]. /// ```rust @@ -240,16 +241,14 @@ pub mod u32 { use std::result::Result; serde_conv_doc!( - /// 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. - /// + /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes + /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32, Timestamp}; - /// # use serde_with::serde_as; + /// use bson::{serde_helpers::datetime, DateTime}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index d2f11e31..9d526425 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -832,6 +832,8 @@ fn test_oid_helpers() { #[test] fn test_datetime_helpers() { + use time::OffsetDateTime; + let _guard = LOCK.run_concurrently(); #[cfg(feature = "serde_with-3")] @@ -1434,7 +1436,7 @@ fn test_u32_helpers() { ); #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, PartialEq, Debug)] struct C { #[serde_as(as = "u32::AsF64")] pub value: u32, @@ -1489,30 +1491,11 @@ fn test_u32_helpers() { "Expected each serialized element in value_vector to match the original." ); - // Deserialize the BSON back to the struct - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized.value, value, - "Expected deserialized value to match the original." - ); - - assert_eq!( - c_deserialized.value_optional_none, None, - "Expected deserialized val_optional_none to be None." - ); - - assert_eq!( - c_deserialized.value_optional_some, - Some(value), - "Expected deserialized val_optional_some to match the original." - ); - - assert_eq!( - c_deserialized.value_vector, - vec![value], - "Expected deserialized val_vector to match the original." + c_deserialized, c, + "Deserialized struct does not match original." ); #[serde_as] From ae2b0a40ad6b1504ae997703556282a213b59dde Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 15:53:34 -0400 Subject: [PATCH 34/61] Fix rustdoc imports --- src/serde_helpers.rs | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index a3aa2845..c8270cbd 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -277,9 +277,9 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u32; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::u32; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { @@ -310,9 +310,9 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u32; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::u32; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct FileInfo { @@ -342,9 +342,9 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32}; - /// # use serde_with::serde_as; + /// use bson::{serde_helpers::u32}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -370,9 +370,9 @@ pub mod u32 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u32}; - /// # use serde_with::serde_as; + /// use bson::{serde_helpers::u32}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -409,9 +409,9 @@ pub mod u64 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::u64; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::u64; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct FileInfo { @@ -445,9 +445,9 @@ pub mod u64 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u64}; - /// # use serde_with::serde_as; + /// use bson::{serde_helpers::u64}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -473,9 +473,9 @@ pub mod u64 { /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::{serde_helpers::u64}; - /// # use serde_with::serde_as; + /// use bson::{serde_helpers::u64}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { From 3a0e345717f189e52e0579bd8c20bae8ef11edda Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 16:08:46 -0400 Subject: [PATCH 35/61] Update rustdocs phrasing and improve struct naming for Chrono04DateTime --- README.md | 4 ++-- src/datetime.rs | 4 ++-- src/lib.rs | 4 ++-- src/serde_helpers.rs | 32 +++++++++++++++----------------- src/tests/serde.rs | 8 ++++---- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c4549dfe..204879b5 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ provides a `DateTime` type, but its `Serialize` and `Deserialize` implementation instead, so when using it with BSON, the BSON datetime type is not used. To work around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of convenient conversions between `bson::DateTime` and `chrono::DateTime`, including the -[`datetime::FromChronoDateTime`](https://docs.rs/bson/latest/bson/serde_helpers/datetime/struct.FromChronoDateTime/index.html) +[`datetime::FromChrono04DateTime`](https://docs.rs/bson/latest/bson/serde_helpers/datetime/struct.FromChrono04DateTime/index.html) serde helper, which can be used to (de)serialize `chrono::DateTime`s to/from BSON datetimes, and the `From` implementation for `Bson`, which allows `chrono::DateTime` values to be used in the `doc!` and `bson!` macros. @@ -250,7 +250,7 @@ struct Foo { // serializes as a BSON datetime. // this requires the "chrono-0_4" feature flag - #[serde_as(as = "bson::serde_helpers::datetime::FromChronoDateTime")] + #[serde_as(as = "bson::serde_helpers::datetime::FromChrono04DateTime")] chrono_as_bson: chrono::DateTime, } diff --git a/src/datetime.rs b/src/datetime.rs index ccecefea..1ea6cb1f 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -110,7 +110,7 @@ use crate::error::{Error, Result}; /// The `bson` crate provides a number of useful helpers for serializing and deserializing /// 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::FromChronoDateTime`]. +/// [`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 @@ -134,7 +134,7 @@ use crate::error::{Error, Result}; /// /// // serializes as a BSON datetime. /// // this requires the "chrono-0_4" feature flag -/// #[serde_as(as = "datetime::FromChronoDateTime")] +/// #[serde_as(as = "datetime::FromChrono04DateTime")] /// chrono_as_bson: chrono::DateTime, /// /// // serializes as an RFC 3339 / ISO-8601 string. diff --git a/src/lib.rs b/src/lib.rs index bbb1785f..fa4c9495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ //! on strings instead, so when using it with BSON, the BSON datetime type is not used. To work //! around this, the `chrono-0_4` feature flag can be enabled. This flag exposes a number of //! convenient conversions between [`bson::DateTime`](crate::DateTime) and [`chrono::DateTime`], -//! including the [`serde_helpers::datetime::FromChronoDateTime`] +//! including the [`serde_helpers::datetime::FromChrono04DateTime`] //! serde helper, which can be used to (de)serialize [`chrono::DateTime`]s to/from BSON datetimes, //! and the `From` implementation for [`Bson`], which allows [`chrono::DateTime`] //! values to be used in the `doc!` and `bson!` macros. @@ -258,7 +258,7 @@ //! //! // serializes as a BSON datetime. //! // this requires the "chrono-0_4" feature flag -//! #[serde_as(as = "datetime::FromChronoDateTime")] +//! #[serde_as(as = "datetime::FromChrono04DateTime")] //! chrono_as_bson: chrono::DateTime, //! } //! diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 63d2cf32..78d032c1 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -223,11 +223,13 @@ pub mod u64_as_f64 { /// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`datetime::AsRfc3339String`] — serializes a [`crate::DateTime`] as a RFC 3339 string. -/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`crate::DateTime`]. -/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`crate::DateTime`]. -/// - [`datetime::FromI64`] — serializes a `i64` as a [`crate::DateTime`]. -/// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a +/// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 string. +/// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a +/// [`crate::DateTime`]. +/// - [`datetime::FromChrono04DateTime`] — converts a [`chrono::DateTime`] to and from a +/// [`crate::DateTime`]. +/// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. +/// - [`datetime::FromTime03OffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a /// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] @@ -239,8 +241,7 @@ pub mod datetime { use std::result::Result; serde_conv_doc!( - /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes - /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. + /// Converts a [`DateTime`] to and from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -268,8 +269,7 @@ pub mod datetime { ); serde_conv_doc!( - /// Serializes an RFC 3339 (ISO 8601) formatted string as a [`DateTime`] and deserializes an - /// RFC 3339 (ISO 8601) formatted string from a [`DateTime`]. + /// Converts an RFC 3339 (ISO 8601) formatted string to and from a [`DateTime`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -298,8 +298,7 @@ pub mod datetime { serde_conv_doc!( #[cfg(feature = "chrono-0_4")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] - /// Serializes a [`chrono::DateTime`] as a [`DateTime`] and deserializes a [`chrono::DateTime`] - /// from a [`DateTime`]. + /// Converts a [`chrono::DateTime`] to and from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { @@ -309,12 +308,12 @@ pub mod datetime { /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "datetime::FromChronoDateTime")] + /// #[serde_as(as = "datetime::FromChrono04DateTime")] /// pub date: chrono::DateTime, /// } /// # } /// ``` - pub FromChronoDateTime, + pub FromChrono04DateTime, chrono::DateTime, |chrono_date: &chrono::DateTime| -> Result { Ok(DateTime::from_chrono(*chrono_date)) @@ -325,9 +324,9 @@ pub mod datetime { ); serde_conv_doc!( - /// Serializes a `i64` integer as [`DateTime`] and deserializes a `i64` integer from [`DateTime`]. + /// Converts an `i64` integer to and from a [`DateTime`]. /// - /// The `i64` should represent seconds `(DateTime::timestamp_millis(..))`. + /// The `i64` should represent milliseconds. See [`DateTime::from_millis`] for more details. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -355,8 +354,7 @@ pub mod datetime { serde_conv_doc!( #[cfg(feature = "time-0_3")] #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] - /// Serializes a [`time::OffsetDateTime`] as a [`DateTime`] and deserializes a - /// [`time::OffsetDateTime`] from a [`DateTime`]. + /// Converts a [`time::OffsetDateTime`] to and from a [`DateTime`]. /// ```rust /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] /// # { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 384665f2..d63896f9 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -983,16 +983,16 @@ fn test_datetime_helpers() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "datetime::FromChronoDateTime")] + #[serde_as(as = "datetime::FromChrono04DateTime")] pub date: chrono::DateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option>, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option>, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec>, } From f527aff5b844a72f672113be550a40b4795ac3ea Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 16:23:42 -0400 Subject: [PATCH 36/61] Split tests for DateTime --- src/serde_helpers.rs | 54 ++-- src/tests/serde.rs | 628 ++++++++++++++++++++++--------------------- 2 files changed, 345 insertions(+), 337 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 78d032c1..fcf97f99 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -226,9 +226,9 @@ pub mod u64_as_f64 { /// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 string. /// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a /// [`crate::DateTime`]. +/// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. /// - [`datetime::FromChrono04DateTime`] — converts a [`chrono::DateTime`] to and from a /// [`crate::DateTime`]. -/// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. /// - [`datetime::FromTime03OffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a /// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] @@ -296,58 +296,58 @@ pub mod datetime { ); serde_conv_doc!( - #[cfg(feature = "chrono-0_4")] - #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] - /// Converts a [`chrono::DateTime`] to and from a [`DateTime`]. + /// Converts an `i64` integer to and from a [`DateTime`]. + /// + /// The `i64` should represent milliseconds. See [`DateTime::from_millis`] for more details. /// ```rust - /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] + /// # #[cfg(feature = "serde_with-3")] /// # { /// use bson::serde_helpers::datetime; /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] - /// struct Event { - /// #[serde_as(as = "datetime::FromChrono04DateTime")] - /// pub date: chrono::DateTime, + /// struct Item { + /// #[serde_as(as = "datetime::FromI64")] + /// pub now: i64, /// } /// # } /// ``` - pub FromChrono04DateTime, - chrono::DateTime, - |chrono_date: &chrono::DateTime| -> Result { - Ok(DateTime::from_chrono(*chrono_date)) + pub FromI64, + i64, + |value: &i64| -> Result { + Ok(DateTime::from_millis(*value)) }, - |bson_date: DateTime| -> Result, String> { - Ok(bson_date.to_chrono()) + |date: DateTime| -> Result { + Ok(date.timestamp_millis()) } ); serde_conv_doc!( - /// Converts an `i64` integer to and from a [`DateTime`]. - /// - /// The `i64` should represent milliseconds. See [`DateTime::from_millis`] for more details. + #[cfg(feature = "chrono-0_4")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] + /// Converts a [`chrono::DateTime`] to and from a [`DateTime`]. /// ```rust - /// # #[cfg(feature = "serde_with-3")] + /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] /// # { /// use bson::serde_helpers::datetime; /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] - /// struct Item { - /// #[serde_as(as = "datetime::FromI64")] - /// pub now: i64, + /// struct Event { + /// #[serde_as(as = "datetime::FromChrono04DateTime")] + /// pub date: chrono::DateTime, /// } /// # } /// ``` - pub FromI64, - i64, - |value: &i64| -> Result { - Ok(DateTime::from_millis(*value)) + pub FromChrono04DateTime, + chrono::DateTime, + |chrono_date: &chrono::DateTime| -> Result { + Ok(DateTime::from_chrono(*chrono_date)) }, - |date: DateTime| -> Result { - Ok(date.timestamp_millis()) + |bson_date: DateTime| -> Result, String> { + Ok(bson_date.to_chrono()) } ); diff --git a/src/tests/serde.rs b/src/tests/serde.rs index d63896f9..1bbde432 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -741,375 +741,383 @@ fn test_unsigned_helpers() { } #[test] -fn test_datetime_helpers() { - use time::OffsetDateTime; - +#[cfg(feature = "serde_with-3")] +fn test_datetime_rfc3339_string_helpers() { let _guard = LOCK.run_concurrently(); - #[cfg(feature = "serde_with-3")] - { - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "datetime::AsRfc3339String")] - pub date: DateTime, + #[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 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 - ); + // 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(Serialize, Deserialize, Debug, PartialEq)] - struct B { - #[serde_as(as = "datetime::FromI64")] - date: i64, + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "datetime::FromRfc3339String")] + pub date: String, - #[serde_as(as = "Option")] - date_optional_none: Option, + #[serde_as(as = "Option")] + pub date_optional_none: Option, - #[serde_as(as = "Option")] - date_optional_some: Option, + #[serde_as(as = "Option")] + pub date_optional_some: Option, - #[serde_as(as = "Vec")] - date_vector: Vec, - } + #[serde_as(as = "Vec")] + pub date_vector: Vec, + } - let date = DateTime::now(); - let b = B { - date: date.timestamp_millis(), - date_optional_none: None, - date_optional_some: Some(date.timestamp_millis()), - date_vector: vec![date.timestamp_millis()], - }; + 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(); + // 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 match original date." - ); + // 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." - ); + 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." - ); + 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 deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "Deserialized struct does not match original." + ); - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct C { - #[serde_as(as = "datetime::FromRfc3339String")] - pub date: 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 + ); +} - #[serde_as(as = "Option")] - pub date_optional_none: Option, +#[test] +#[cfg(feature = "serde_with-3")] +fn test_datetime_i64_helper() { + let _guard = LOCK.run_concurrently(); - #[serde_as(as = "Option")] - pub date_optional_some: Option, + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromI64")] + date: i64, - #[serde_as(as = "Vec")] - pub date_vector: Vec, - } + #[serde_as(as = "Option")] + date_optional_none: Option, - let date = DateTime::now(); - let c = C { - 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()], - }; + #[serde_as(as = "Option")] + date_optional_some: Option, - // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); + #[serde_as(as = "Vec")] + date_vector: Vec, + } - // Validate serialized data - assert_eq!( - *doc.get_datetime("date").unwrap(), - date, - "Expected serialized date to be a BSON DateTime." - ); + 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()], + }; - assert_eq!( - doc.get("date_optional_none"), - Some(&Bson::Null), - "Expected serialized date_optional_none to be None." - ); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(date)), - "Expected serialized date_optional_some to match original." - ); + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap(), + &date, + "Expected serialized date to match original date." + ); - 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." - ); + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); - // Validate deserialized data - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - assert_eq!( - c_deserialized, c, - "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." + ); - // Validate serializing error case with an invalid DateTime string - let invalid_date = "invalid_date"; - let bad_c = C { - 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_c); - 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 - ); - } + 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." + ); - #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - { - use std::str::FromStr; + // 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] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "datetime::FromChrono04DateTime")] - pub date: chrono::DateTime, +#[test] +#[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] +fn test_datetime_chrono04_datetime_helper() { + let _guard = LOCK.run_concurrently(); - #[serde_as(as = "Option")] - pub date_optional_none: Option>, + use std::str::FromStr; - #[serde_as(as = "Option")] - pub date_optional_some: Option>, + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromChrono04DateTime")] + pub date: chrono::DateTime, - #[serde_as(as = "Vec")] - pub date_vector: Vec>, - } + #[serde_as(as = "Option")] + pub date_optional_none: Option>, - 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], - }; + #[serde_as(as = "Option")] + pub date_optional_some: Option>, - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); + #[serde_as(as = "Vec")] + pub date_vector: Vec>, + } - // Validate serialized data - assert_eq!( - doc.get_datetime("date").unwrap().to_chrono(), - date, - "Expected serialized date to match original 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], + }; - assert_eq!( - doc.get("date_optional_none"), - Some(&Bson::Null), - "Expected serialized date_optional_none to be None." - ); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(DateTime::from_chrono(date))), - "Expected serialized date_optional_some to match original." - ); + // Validate serialized data + assert_eq!( + doc.get_datetime("date").unwrap().to_chrono(), + date, + "Expected serialized date to match original date." + ); - 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." - ); + assert_eq!( + doc.get("date_optional_none"), + Some(&Bson::Null), + "Expected serialized date_optional_none to be None." + ); - // Validate deserialized data - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - assert_eq!( - a_deserialized, a, - "Deserialized struct does not match original." - ); - } + assert_eq!( + doc.get("date_optional_some"), + Some(&Bson::DateTime(DateTime::from_chrono(date))), + "Expected serialized date_optional_some to match original." + ); - #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] - { - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "datetime::FromTime03OffsetDateTime")] - pub date: OffsetDateTime, + 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." + ); - #[serde_as(as = "Option")] - pub date_optional_none: Option, + // 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 = "Option")] - pub date_optional_some: Option, +#[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(); - #[serde_as(as = "Vec")] - pub date_vector: Vec, - } + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "datetime::FromTime03OffsetDateTime")] + pub date: OffsetDateTime, - 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()], - }; + #[serde_as(as = "Option")] + pub date_optional_none: Option, - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); + #[serde_as(as = "Option")] + pub date_optional_some: Option, - // Validate serialized data - assert_eq!( - doc.get_datetime("date").unwrap(), - &date, - "Expected serialized date to match original date." - ); + #[serde_as(as = "Vec")] + pub date_vector: Vec, + } - assert_eq!( - doc.get("date_optional_none"), - Some(&Bson::Null), - "Expected serialized date_optional_none to be None." - ); + 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()], + }; - assert_eq!( - doc.get("date_optional_some"), - Some(&Bson::DateTime(date)), - "Expected serialized date_optional_some to match original." - ); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - 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 serialized data + assert_eq!( + doc.get_datetime("date").unwrap(), + &date, + "Expected serialized date to match original date." + ); - // Validate deserialized data - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - assert_eq!( - a_deserialized, a, - "Deserialized struct does not match original." - ); - } + 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] From f83421439959a1d209dbc0f212dcec9c334d0a0b Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 16:38:58 -0400 Subject: [PATCH 37/61] Add back deleted u32 tests from merge --- src/tests/serde.rs | 428 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 428 insertions(+) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 48bed721..1be73a6b 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1210,6 +1210,434 @@ fn test_datetime_time03_offset_datetime_helper() { ); } +#[test] +fn test_u32_helpers() { + let _guard = LOCK.run_concurrently(); + + #[cfg(feature = "serde_with-3")] + { + #[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 { + timestamp, + timestamp_optional_none: None, + timestamp_optional_some: Some(timestamp), + timestamp_vector: vec![timestamp], + }; + + // 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 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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B { + #[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 { + 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(); + + // 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 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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct C { + #[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 value = 12345; + let c = C { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); + + 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 c_deserialized: C = deserialize_from_document(doc).unwrap(); + assert_eq!( + c_deserialized, c, + "Deserialized struct does not match original." + ); + + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct D { + #[serde_as(as = "u32::AsI32")] + value: u32, + + #[serde_as(as = "Option")] + value_optional_none: Option, + + #[serde_as(as = "Option")] + value_optional_some: Option, + + #[serde_as(as = "Vec")] + value_vector: Vec, + } + + let value = 1; + let d = D { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&d).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." + ); + + 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::Int32(value as i32)), + "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::Int32(value as i32)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); + + // Validate deserialized data + let d_deserialized: D = deserialize_from_document(doc).unwrap(); + assert_eq!( + d_deserialized, d, + "Deserialized struct does not match original." + ); + + // Validate serialization fails because u32::MAX is too large to fit in i32 + let invalid_value_for_serializing = u32::MAX; + let bad_d: D = D { + 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_d); + assert!( + result.is_err(), + "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("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_d = 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_d); + 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 + ); + + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct E { + #[serde_as(as = "u32::AsI64")] + value: u32, + + #[serde_as(as = "Option")] + value_optional_none: Option, + + #[serde_as(as = "Option")] + value_optional_some: Option, + + #[serde_as(as = "Vec")] + value_vector: Vec, + } + + let value = u32::MAX; + let e = E { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&e).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." + ); + + 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::Int64(value as i64)), + "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::Int64(value as i64)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); + + // Validate deserialized data + let e_deserialized: E = deserialize_from_document(doc).unwrap(); + assert_eq!( + e_deserialized, e, + "Round-trip failed: deserialized struct did not match original." + ); + + // Validate deserialization fails for i64::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i64::MIN; + let bad_e = 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_e); + assert!( + result.is_err(), + "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("Cannot convert i64"), + "Expected error message to mention failed i64 to u32 conversion, got: {}", + err_string + ); + } +} + #[test] fn test_u64_helpers() { let _guard = LOCK.run_concurrently(); From ec82af42841ba6bc203afd78348ac8c7e24d3f39 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 16:42:12 -0400 Subject: [PATCH 38/61] Fix datetime rustdoc comments from merge --- src/serde_helpers.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 1fd5011b..a31dc6f5 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -73,11 +73,14 @@ pub mod object_id { /// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`datetime::AsRfc3339String`] — serializes a [`crate::DateTime`] as a RFC 3339 string. -/// - [`datetime::FromRfc3339String`] — serializes a RFC 3339 string as a [`crate::DateTime`]. -/// - [`datetime::FromChronoDateTime`] — serializes a [`chrono::DateTime`] as a [`crate::DateTime`]. -/// - [`datetime::FromI64`] — serializes a `i64` as a [`crate::DateTime`]. -/// - [`datetime::FromTime03OffsetDateTime`] — serializes a [`time::OffsetDateTime`] as a +/// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 +/// string. +/// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a +/// [`crate::DateTime`]. +/// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. +/// - [`datetime::FromChrono04DateTime`] — converts a [`chrono::DateTime`] to and from a +/// [`crate::DateTime`]. +/// - [`datetime::FromTime03OffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a /// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] @@ -89,8 +92,7 @@ pub mod datetime { use std::result::Result; serde_conv_doc!( - /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes - /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. + /// Converts a [`DateTime`] to and from an RFC 3339 (ISO 8601) formatted string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { From 47cbddd89294a7542a264ee9bfda7948330a61f1 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 16:55:15 -0400 Subject: [PATCH 39/61] Update rustdoc comments --- src/serde_helpers.rs | 57 ++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index a31dc6f5..d581baa8 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -231,6 +231,14 @@ pub mod datetime { ); } +/// Type converters for serializing and deserializing `u32` using [`serde_with::serde_as`]. +/// +/// ## Available converters +/// - [`u32::FromTimestamp`] — converts a [`Timestamp`] to and from a `u32`. +/// - [`u32::AsTimestamp`] — converts a `u32` to and from a [`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 { @@ -240,8 +248,11 @@ pub mod u32 { use std::result::Result; serde_conv_doc!( - /// Serializes a [`DateTime`] as an RFC 3339 (ISO 8601) formatted string and deserializes - /// a [`DateTime`] from an RFC 3339 (ISO 8601) formatted string. + /// 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")] /// # { @@ -270,9 +281,11 @@ pub mod u32 { ); serde_conv_doc!( - /// 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. + /// 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")] /// # { @@ -301,8 +314,7 @@ pub mod u32 { ); serde_conv_doc!( - /// Contains functions to serialize a `u32` as an `f64` (BSON double) and deserialize a - /// `u32` from an `f64` (BSON double). + /// Converts a `u32` to and from an `f64`. /// /// Deserialization errors if an exact conversion is not possible. /// @@ -329,15 +341,15 @@ pub mod u32 { if (value - value as u32 as f64).abs() <= f64::EPSILON { Ok(value as u32) } else { - Err(format!("Cannot convert f64 (BSON double) {} to u32", value)) + Err(format!("Cannot convert f64 {} to u32", value)) } } ); serde_conv_doc!( - /// Contains functions to serialize a `u32` as a `i32` and deserialize a `u32` - /// from a `i32`. Errors if an exact conversion is not possible. + /// Converts a `u32` to and from an `i32`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -363,9 +375,9 @@ pub mod u32 { ); serde_conv_doc!( - /// Contains functions to serialize a `u32` as a `i64` and deserialize a `u32` - /// from a `i64`. Errors if an exact conversion is not possible. + /// Converts a `u32` to and from an `i64`. /// + /// Deserialization errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -391,6 +403,12 @@ pub mod u32 { ); } +/// 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 { @@ -400,10 +418,9 @@ pub mod u64 { use std::result::Result; serde_conv_doc!( - /// Contains functions to serialize a `u64` as an `f64` (BSON double) and deserialize a - /// `u64` from an `f64` (BSON double). + /// Converts a `u64` to and from an `f64`. /// - /// Deserialization errors if an exact conversion is not possible. + /// Errors if an exact conversion is not possible. /// /// ```rust /// # #[cfg(feature = "serde_with-3")] @@ -425,22 +442,22 @@ pub mod u64 { if value < &u64::MAX && *value == *value as f64 as u64 { Ok(*value as f64) } else { - Err(format!("Cannot convert u64 {} to f64 (BSON double)", value)) + 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 (BSON double) {} to u64", value)) + Err(format!("Cannot convert f64 {} to u64", value)) } } ); serde_conv_doc!( - /// Contains functions to serialize a `u64` as a `i32` and deserialize a `u64` - /// from a `i32`. Errors if an exact conversion is not possible. + /// Converts a `u64` to and from an `i32`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -466,9 +483,9 @@ pub mod u64 { ); serde_conv_doc!( - /// Contains functions to serialize a `u64` as a `i64` and deserialize a `u64` - /// from a `i64`. Errors if an exact conversion is not possible. + /// Converts a `u64` to and from an `i64`. /// + /// Errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { From fe5b5e424c56006ea9cf8c7179c4c3a6ea7d2e4a Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 17:02:23 -0400 Subject: [PATCH 40/61] clippy test: revert FromTime03OffsetDateTime to FromTimeOffsetDateTime --- src/serde_helpers.rs | 6 +++--- src/tests/serde.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index fcf97f99..9640a856 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -229,7 +229,7 @@ pub mod u64_as_f64 { /// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. /// - [`datetime::FromChrono04DateTime`] — converts a [`chrono::DateTime`] to and from a /// [`crate::DateTime`]. -/// - [`datetime::FromTime03OffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a +/// - [`datetime::FromTimeOffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a /// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] @@ -364,12 +364,12 @@ pub mod datetime { /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] + /// #[serde_as(as = "datetime::FromTimeOffsetDateTime")] /// pub date: time::OffsetDateTime, /// } /// # } /// ``` - pub FromTime03OffsetDateTime, + pub FromTimeOffsetDateTime, time::OffsetDateTime, |value: &time::OffsetDateTime| -> Result { Ok(DateTime::from_time_0_3(*value)) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 1bbde432..e2732166 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1059,16 +1059,16 @@ fn test_datetime_time03_offset_datetime_helper() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "datetime::FromTime03OffsetDateTime")] + #[serde_as(as = "datetime::FromTimeOffsetDateTime")] pub date: OffsetDateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } From ca6f68fcdbac17317edf06ac52e1d20b67c2cff4 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 17:51:43 -0400 Subject: [PATCH 41/61] Rename FromTime03OffsetDateTime --- src/serde_helpers.rs | 6 +++--- src/tests/serde.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 9640a856..fcf97f99 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -229,7 +229,7 @@ pub mod u64_as_f64 { /// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. /// - [`datetime::FromChrono04DateTime`] — converts a [`chrono::DateTime`] to and from a /// [`crate::DateTime`]. -/// - [`datetime::FromTimeOffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a +/// - [`datetime::FromTime03OffsetDateTime`] — converts a [`time::OffsetDateTime`] to and from a /// [`crate::DateTime`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] @@ -364,12 +364,12 @@ pub mod datetime { /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Event { - /// #[serde_as(as = "datetime::FromTimeOffsetDateTime")] + /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] /// pub date: time::OffsetDateTime, /// } /// # } /// ``` - pub FromTimeOffsetDateTime, + pub FromTime03OffsetDateTime, time::OffsetDateTime, |value: &time::OffsetDateTime| -> Result { Ok(DateTime::from_time_0_3(*value)) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index e2732166..1bbde432 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1059,16 +1059,16 @@ fn test_datetime_time03_offset_datetime_helper() { #[serde_as] #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { - #[serde_as(as = "datetime::FromTimeOffsetDateTime")] + #[serde_as(as = "datetime::FromTime03OffsetDateTime")] pub date: OffsetDateTime, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_none: Option, - #[serde_as(as = "Option")] + #[serde_as(as = "Option")] pub date_optional_some: Option, - #[serde_as(as = "Vec")] + #[serde_as(as = "Vec")] pub date_vector: Vec, } From 53090e9df733679070ecc7f993b06b32a3e17c36 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 18:16:19 -0400 Subject: [PATCH 42/61] Revert all UUID changes (separate to another PR) --- src/serde_helpers.rs | 287 +++++++++++++++++------------------ src/tests/serde.rs | 347 ++++++++----------------------------------- src/uuid.rs | 7 +- 3 files changed, 199 insertions(+), 442 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index d581baa8..49e355a1 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -1,11 +1,36 @@ //! 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::{ marker::PhantomData, ops::{Deref, DerefMut}, result::Result, }; +#[cfg(feature = "uuid-1")] +#[doc(inline)] +pub use uuid_1_as_binary::{ + deserialize as deserialize_uuid_1_from_binary, + serialize as serialize_uuid_1_as_binary, +}; +#[cfg(feature = "uuid-1")] +#[doc(inline)] +pub use uuid_1_as_c_sharp_legacy_binary::{ + deserialize as deserialize_uuid_1_from_c_sharp_legacy_binary, + serialize as serialize_uuid_1_as_c_sharp_legacy_binary, +}; +#[cfg(feature = "uuid-1")] +#[doc(inline)] +pub use uuid_1_as_java_legacy_binary::{ + deserialize as deserialize_uuid_1_from_java_legacy_binary, + serialize as serialize_uuid_1_as_java_legacy_binary, +}; +#[cfg(feature = "uuid-1")] +#[doc(inline)] +pub use uuid_1_as_python_legacy_binary::{ + deserialize as deserialize_uuid_1_from_python_legacy_binary, + serialize as serialize_uuid_1_as_python_legacy_binary, +}; + #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -73,8 +98,7 @@ pub mod object_id { /// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 -/// string. +/// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 string. /// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a /// [`crate::DateTime`]. /// - [`datetime::FromI64`] — converts an `i64` to and from a [`crate::DateTime`]. @@ -511,156 +535,6 @@ pub mod u64 { ); } -#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "serde_with-3", feature = "uuid-1"))))] -pub mod uuid_1 { - use crate::macros::serde_conv_doc; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; - use uuid::Uuid; - - serde_conv_doc!( - /// Contains functions to serialize a [`uuid::Uuid`] as a [`crate::Binary`] and deserialize a - /// [`uuid::Uuid`] from a [`crate::Binary`]. - /// - /// ```rust - /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] - /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; - /// use bson::serde_helpers::uuid_1; - /// use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Item { - /// #[serde_as(as = "uuid_1::AsBinary")] - /// pub id: Uuid, - /// } - /// # } - /// ``` - pub AsBinary, - Uuid, - |uuid: &Uuid| -> Result { - Ok(crate::uuid::Uuid::from(*uuid)) - }, - |bson_uuid: crate::uuid::Uuid| -> Result { - Ok(bson_uuid.into()) - } - ); - - serde_conv_doc!( - /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy C# driver - /// UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy C# driver - /// format. - /// - /// ```rust - /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] - /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; - /// use bson::serde_helpers::uuid_1; - /// use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Item { - /// #[serde_as(as = "uuid_1::AsCSharpLegacyBinary")] - /// pub id: Uuid, - /// } - /// # } - /// ``` - pub AsCSharpLegacyBinary, - Uuid, - |uuid: &Uuid| -> Result { - let inner = crate::uuid::Uuid::from(*uuid); - Ok(crate::Binary::from_uuid_with_representation( - inner, - crate::uuid::UuidRepresentation::CSharpLegacy, - )) - }, - |binary: crate::Binary| -> Result { - let inner = binary - .to_uuid_with_representation(crate::uuid::UuidRepresentation::CSharpLegacy) - .map_err(|e| e.to_string())?; - Ok(inner.into()) - } - ); - - serde_conv_doc!( - /// /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy - /// Java driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy - /// Java driver format. - /// - /// ```rust - /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] - /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; - /// use bson::serde_helpers::uuid_1; - /// # use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Item { - /// #[serde_as(as = "uuid_1::AsJavaLegacyBinary")] - /// pub id: Uuid, - /// } - /// # } - /// ``` - pub AsJavaLegacyBinary, - Uuid, - |uuid: &Uuid| -> Result { - let inner = crate::uuid::Uuid::from(*uuid); - Ok(crate::Binary::from_uuid_with_representation( - inner, - crate::uuid::UuidRepresentation::JavaLegacy, - )) - }, - |binary: crate::Binary| -> Result { - let inner = binary - .to_uuid_with_representation(crate::uuid::UuidRepresentation::JavaLegacy) - .map_err(|e| e.to_string())?; - Ok(inner.into()) - } - ); - - serde_conv_doc!( - /// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy Python - /// driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy Python - /// driver format. - /// - /// ```rust - /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] - /// # { - /// use serde::{Serialize, Deserialize}; - /// use uuid::Uuid; - /// use bson::serde_helpers::uuid_1; - /// # use serde_with::serde_as; - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Item { - /// #[serde_as(as = "uuid_1::AsPythonLegacyBinary")] - /// pub id: Uuid, - /// } - /// # } - /// ``` - pub AsPythonLegacyBinary, - Uuid, - |uuid: &Uuid| -> Result { - let inner = crate::uuid::Uuid::from(*uuid); - Ok(crate::Binary::from_uuid_with_representation( - inner, - crate::uuid::UuidRepresentation::PythonLegacy, - )) - }, - |binary: crate::Binary| -> Result { - let inner = binary - .to_uuid_with_representation(crate::uuid::UuidRepresentation::PythonLegacy) - .map_err(|e| e.to_string())?; - Ok(inner.into()) - } - ); -} - #[allow(unused_macros)] macro_rules! as_binary_mod { ($feat:meta, $uu:path) => { @@ -686,6 +560,29 @@ macro_rules! as_binary_mod { }; } +/// Contains functions to serialize a [`uuid::Uuid`] as a [`crate::Binary`] and deserialize a +/// [`uuid::Uuid`] from a [`crate::Binary`]. +/// +/// ```rust +/// # #[cfg(feature = "uuid-1")] +/// # { +/// use serde::{Serialize, Deserialize}; +/// use uuid::Uuid; +/// use bson::serde_helpers::uuid_1_as_binary; +/// +/// #[derive(Serialize, Deserialize)] +/// struct Item { +/// #[serde(with = "uuid_1_as_binary")] +/// pub id: Uuid, +/// } +/// # } +/// ``` +#[cfg(feature = "uuid-1")] +#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] +pub mod uuid_1_as_binary { + as_binary_mod!(cfg(feature = "uuid-1"), uuid::Uuid); +} + #[allow(unused_macros)] macro_rules! as_legacy_binary_mod { ($feat:meta, $uu:path, $rep:path) => { @@ -716,6 +613,90 @@ macro_rules! as_legacy_binary_mod { }; } +/// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy +/// Java driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy +/// Java driver format. +/// +/// ```rust +/// #[cfg(feature = "uuid-1")] +/// # { +/// use serde::{Serialize, Deserialize}; +/// use uuid::Uuid; +/// use bson::serde_helpers::uuid_1_as_java_legacy_binary; +/// +/// #[derive(Serialize, Deserialize)] +/// struct Item { +/// #[serde(with = "uuid_1_as_java_legacy_binary")] +/// pub id: Uuid, +/// } +/// # } +/// ``` +#[cfg(feature = "uuid-1")] +#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] +pub mod uuid_1_as_java_legacy_binary { + as_legacy_binary_mod!( + cfg(feature = "uuid-1"), + uuid::Uuid, + UuidRepresentation::JavaLegacy + ); +} + +/// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy Python +/// driver UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy Python +/// driver format. +/// +/// ```rust +/// # #[cfg(feature = "uuid-1")] +/// # { +/// use serde::{Serialize, Deserialize}; +/// use uuid::Uuid; +/// use bson::serde_helpers::uuid_1_as_python_legacy_binary; +/// +/// #[derive(Serialize, Deserialize)] +/// struct Item { +/// #[serde(with = "uuid_1_as_python_legacy_binary")] +/// pub id: Uuid, +/// } +/// # } +/// ``` +#[cfg(feature = "uuid-1")] +#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] +pub mod uuid_1_as_python_legacy_binary { + as_legacy_binary_mod!( + cfg(feature = "uuid-1"), + uuid::Uuid, + UuidRepresentation::PythonLegacy + ); +} + +/// Contains functions to serialize a [`uuid::Uuid`] to a [`crate::Binary`] in the legacy C# driver +/// UUID format and deserialize [`uuid::Uuid`] from a [`crate::Binary`] in the legacy C# driver +/// format. +/// +/// ```rust +/// # #[cfg(feature = "uuid-1")] +/// # { +/// use serde::{Serialize, Deserialize}; +/// use uuid::Uuid; +/// use bson::serde_helpers::uuid_1_as_c_sharp_legacy_binary; +/// +/// #[derive(Serialize, Deserialize)] +/// struct Item { +/// #[serde(with = "uuid_1_as_c_sharp_legacy_binary")] +/// pub id: Uuid, +/// } +/// # } +/// ``` +#[cfg(feature = "uuid-1")] +#[cfg_attr(docsrs, doc(cfg(feature = "uuid-1")))] +pub mod uuid_1_as_c_sharp_legacy_binary { + as_legacy_binary_mod!( + cfg(feature = "uuid-1"), + uuid::Uuid, + UuidRepresentation::CSharpLegacy + ); +} + /// 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 1be73a6b..5194bb1a 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::{datetime, object_id, u32, u64, uuid_1}, + serde_helpers::{self, datetime, object_id, u32, u64}, serialize_to_bson, serialize_to_document, spec::BinarySubtype, @@ -24,7 +24,6 @@ use crate::{ use serde::{Deserialize, Serialize}; use serde_json::json; use serde_with::serde_as; -use time::OffsetDateTime; use std::{ collections::BTreeMap, @@ -542,6 +541,54 @@ fn test_de_db_pointer() { assert_eq!(foo.db_pointer, db_pointer.clone()); } +#[cfg(feature = "uuid-1")] +#[test] +fn test_serde_legacy_uuid_1() { + use uuid::Uuid; + + let _guard = LOCK.run_concurrently(); + + #[derive(Serialize, Deserialize)] + struct Foo { + #[serde(with = "serde_helpers::uuid_1_as_java_legacy_binary")] + java_legacy: Uuid, + #[serde(with = "serde_helpers::uuid_1_as_python_legacy_binary")] + python_legacy: Uuid, + #[serde(with = "serde_helpers::uuid_1_as_c_sharp_legacy_binary")] + csharp_legacy: Uuid, + } + let uuid = Uuid::parse_str("00112233445566778899AABBCCDDEEFF").unwrap(); + let foo = Foo { + java_legacy: uuid, + python_legacy: uuid, + csharp_legacy: uuid, + }; + + let x = serialize_to_bson(&foo).unwrap(); + assert_eq!( + x.as_document().unwrap(), + &doc! { + "java_legacy": Bson::Binary(Binary{ + subtype:BinarySubtype::UuidOld, + bytes: hex::decode("7766554433221100FFEEDDCCBBAA9988").unwrap(), + }), + "python_legacy": Bson::Binary(Binary{ + subtype:BinarySubtype::UuidOld, + bytes: hex::decode("00112233445566778899AABBCCDDEEFF").unwrap(), + }), + "csharp_legacy": Bson::Binary(Binary{ + subtype:BinarySubtype::UuidOld, + bytes: hex::decode("33221100554477668899AABBCCDDEEFF").unwrap(), + }) + } + ); + + let foo: Foo = deserialize_from_bson(x).unwrap(); + assert_eq!(foo.java_legacy, uuid); + assert_eq!(foo.python_legacy, uuid); + assert_eq!(foo.csharp_legacy, uuid); +} + #[test] fn test_de_uuid_extjson_string() { let _guard = LOCK.run_concurrently(); @@ -1955,304 +2002,34 @@ fn test_u64_helpers() { } } -#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] #[test] +#[cfg(feature = "uuid-1")] fn test_uuid_1_helpers() { - use crate::uuid::UuidRepresentation; + use serde_helpers::uuid_1_as_binary; use uuid::Uuid; let _guard = LOCK.run_concurrently(); - fn assert_binary_match( - actual: &Bson, - uuid: &Uuid, - expected_subtype: BinarySubtype, - uuid_representation: UuidRepresentation, - ) { - match actual { - Bson::Binary(Binary { subtype, bytes }) => { - assert_eq!( - subtype, &expected_subtype, - "Expected subtype {:?}, but got {:?}", - expected_subtype, subtype - ); - let expected_bytes = { - let uuid: crate::Uuid = crate::uuid::Uuid::from(*uuid); - crate::Binary::from_uuid_with_representation(uuid, uuid_representation).bytes - }; - assert_eq!( - bytes, &expected_bytes, - "Serialized binary bytes did not match for representation {:?}", - uuid_representation - ); - } - other => panic!("Expected Bson::Binary, got {:?}", other), - } - } + #[derive(Serialize, Deserialize)] - #[serde_as] - #[derive(Serialize, Deserialize, Debug, PartialEq)] struct A { - #[serde_as(as = "uuid_1::AsBinary")] + #[serde(with = "uuid_1_as_binary")] uuid: Uuid, - - #[serde_as(as = "Option")] - uuid_optional_none: Option, - - #[serde_as(as = "Option")] - uuid_optional_some: Option, - - #[serde_as(as = "Vec")] - uuid_vector: Vec, } let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let a: A = A { - uuid, - uuid_optional_none: None, - uuid_optional_some: Some(uuid), - uuid_vector: vec![uuid], - }; + let a = A { uuid }; - // Serialize the struct to BSON let doc = serialize_to_document(&a).unwrap(); - - // Validate serialized data - assert_binary_match( - doc.get("uuid").unwrap(), - &uuid, - BinarySubtype::Uuid, - UuidRepresentation::Standard, - ); - - assert_eq!( - doc.get("uuid_optional_none"), - Some(&Bson::Null), - "Expected serialized uuid_optional_none to be None." - ); - - assert_binary_match( - doc.get("uuid_optional_some").unwrap(), - &uuid, - BinarySubtype::Uuid, - UuidRepresentation::Standard, - ); - - let uuid_vector = doc - .get_array("uuid_vector") - .expect("Expected serialized uuid_vector to be a BSON array."); - assert_eq!(uuid_vector.len(), 1); - assert_binary_match( - &uuid_vector[0], - &uuid, - BinarySubtype::Uuid, - UuidRepresentation::Standard, - ); - - // 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] - #[derive(Serialize, Deserialize, Debug, PartialEq)] - struct B { - #[serde_as(as = "uuid_1::AsCSharpLegacyBinary")] - uuid: Uuid, - - #[serde_as(as = "Option")] - uuid_optional_none: Option, - - #[serde_as(as = "Option")] - uuid_optional_some: Option, - - #[serde_as(as = "Vec")] - uuid_vector: Vec, - } - - let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let b: B = B { - uuid, - uuid_optional_none: None, - uuid_optional_some: Some(uuid), - uuid_vector: vec![uuid], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&b).unwrap(); - - // Validate serialized data - assert_binary_match( - doc.get("uuid").unwrap(), - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::CSharpLegacy, - ); - - assert_eq!( - doc.get("uuid_optional_none"), - Some(&Bson::Null), - "Expected serialized uuid_optional_none to be None." - ); - - assert_binary_match( - doc.get("uuid_optional_some").unwrap(), - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::CSharpLegacy, - ); - - let uuid_vector = doc - .get_array("uuid_vector") - .expect("Expected uuid_vector to be a BSON array"); - assert_eq!(uuid_vector.len(), 1); - assert_binary_match( - &uuid_vector[0], - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::CSharpLegacy, - ); - - // Validate deserialized data - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - assert_eq!( - b_deserialized, b, - "Deserialized struct does not match original." - ); - - #[serde_as] - #[derive(Serialize, Deserialize, Debug, PartialEq)] - struct C { - #[serde_as(as = "uuid_1::AsJavaLegacyBinary")] - uuid: Uuid, - - #[serde_as(as = "Option")] - uuid_optional_none: Option, - - #[serde_as(as = "Option")] - uuid_optional_some: Option, - - #[serde_as(as = "Vec")] - uuid_vector: Vec, - } - - let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let c: C = C { - uuid, - uuid_optional_none: None, - uuid_optional_some: Some(uuid), - uuid_vector: vec![uuid], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); - - // Validate serialized data - assert_binary_match( - doc.get("uuid").unwrap(), - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::JavaLegacy, - ); - - assert_eq!( - doc.get("uuid_optional_none"), - Some(&Bson::Null), - "Expected serialized uuid_optional_none to be None." - ); - - assert_binary_match( - doc.get("uuid_optional_some").unwrap(), - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::JavaLegacy, - ); - - let uuid_vector = doc - .get_array("uuid_vector") - .expect("Expected uuid_vector to be a BSON array"); - assert_eq!(uuid_vector.len(), 1); - assert_binary_match( - &uuid_vector[0], - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::JavaLegacy, - ); - - // Validate deserialized data - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - assert_eq!( - c_deserialized, c, - "Deserialized struct does not match original." - ); - - #[serde_as] - #[derive(Serialize, Deserialize, Debug, PartialEq)] - struct D { - #[serde_as(as = "uuid_1::AsPythonLegacyBinary")] - uuid: Uuid, - - #[serde_as(as = "Option")] - uuid_optional_none: Option, - - #[serde_as(as = "Option")] - uuid_optional_some: Option, - - #[serde_as(as = "Vec")] - uuid_vector: Vec, + match doc.get("uuid").unwrap() { + Bson::Binary(bin) => { + assert_eq!(bin.subtype, BinarySubtype::Uuid); + assert_eq!(bin.bytes, uuid.as_bytes()); + } + _ => panic!("expected Bson::Binary"), } - - let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let d: D = D { - uuid, - uuid_optional_none: None, - uuid_optional_some: Some(uuid), - uuid_vector: vec![uuid], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&d).unwrap(); - - // Validate serialized data - assert_binary_match( - doc.get("uuid").unwrap(), - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::PythonLegacy, - ); - - assert_eq!( - doc.get("uuid_optional_none"), - Some(&Bson::Null), - "Expected serialized uuid_optional_none to be None." - ); - - assert_binary_match( - doc.get("uuid_optional_some").unwrap(), - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::PythonLegacy, - ); - - let uuid_vector = doc - .get_array("uuid_vector") - .expect("Expected uuid_vector to be a BSON array"); - assert_eq!(uuid_vector.len(), 1); - assert_binary_match( - &uuid_vector[0], - &uuid, - BinarySubtype::UuidOld, - UuidRepresentation::PythonLegacy, - ); - - // Validate deserialized data - let d_deserialized: D = deserialize_from_document(doc).unwrap(); - assert_eq!( - d_deserialized, d, - "Deserialized struct does not match original." - ); + let a: A = deserialize_from_document(doc).unwrap(); + assert_eq!(a.uuid, uuid); } #[test] diff --git a/src/uuid.rs b/src/uuid.rs index 3bf464ed..c747fffb 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -19,13 +19,12 @@ //! e.g. //! //! ``` rust -//! # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] +//! # #[cfg(feature = "uuid-1")] //! # { //! # use uuid as uuid; //! use serde::{Serialize, Deserialize}; //! use bson::doc; //! -//! #[serde_as] //! #[derive(Serialize, Deserialize)] //! struct Foo { //! /// serializes as a String or subtype 0 BSON binary, depending @@ -36,8 +35,8 @@ //! bson_uuid: bson::Uuid, //! //! /// serializes as a BSON binary with subtype 4 when either is used. -//! /// this requires the "uuid-1" and "serde_with-3" feature flags -//! #[serde_as(as = "bson::serde_helpers::uuid_1::AsBinary")] +//! /// this requires the "uuid-1" feature flag +//! #[serde(with = "bson::serde_helpers::uuid_1_as_binary")] //! uuid_as_bson: uuid::Uuid, //! } //! # }; From f0c383324179e3815be96872179c50943d34d92b Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 18:20:19 -0400 Subject: [PATCH 43/61] Fix rustdoc import --- src/serde_helpers.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 49e355a1..816455e7 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -96,7 +96,6 @@ pub mod object_id { /// Type converters for serializing and deserializing [`crate::DateTime`] using /// [`serde_with::serde_as`]. -/// /// ## Available converters /// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 string. /// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a @@ -256,10 +255,9 @@ pub mod datetime { } /// Type converters for serializing and deserializing `u32` using [`serde_with::serde_as`]. -/// /// ## Available converters -/// - [`u32::FromTimestamp`] — converts a [`Timestamp`] to and from a `u32`. -/// - [`u32::AsTimestamp`] — converts a `u32` to and from a [`Timestamp`]. +/// - [`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`. @@ -341,7 +339,6 @@ pub mod u32 { /// Converts a `u32` to and from an `f64`. /// /// Deserialization errors if an exact conversion is not possible. - /// /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -428,7 +425,6 @@ pub mod u32 { } /// 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`. From 00a4f9c44fc1e3d4875ca09bcd0aadf7dbd45400 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 18:35:38 -0400 Subject: [PATCH 44/61] Copy documentation and test changes for ObjectId --- src/serde_helpers.rs | 23 +-- src/tests/serde.rs | 364 +++++++++++++++++-------------------------- 2 files changed, 156 insertions(+), 231 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 816455e7..0ac05d81 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -31,6 +31,10 @@ pub use uuid_1_as_python_legacy_binary::{ serialize as serialize_uuid_1_as_python_legacy_binary, }; +/// Type converters for serializing and deserializing [`crate::ObjectId`] using +/// [`serde_with::serde_as`]. ## Available converters +/// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. +/// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -39,15 +43,13 @@ pub mod object_id { use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// Contains functions to serialize an ObjectId as a hex string and deserialize an - /// ObjectId from a hex string + /// Converts an [`ObjectId`] to and from a hex string. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::object_id; - /// # use serde_with::serde_as; - /// # use bson::oid::ObjectId; + /// use bson::{serde_helpers::object_id, oid::ObjectId}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -67,14 +69,13 @@ pub mod object_id { ); serde_conv_doc!( - /// Contains functions to serialize a hex string as an ObjectId and deserialize a - /// hex string from an ObjectId + /// Converts a hex string to and from an [`ObjectId`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::object_id; - /// # use serde_with::serde_as; + /// use bson::serde_helpers::object_id; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 5194bb1a..527c35de 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -652,229 +652,172 @@ fn test_serialize_deserialize_unsigned_numbers() { } #[test] +#[cfg(feature = "serde_with-3")] fn test_oid_helpers() { let _guard = LOCK.run_concurrently(); - #[cfg(feature = "serde_with-3")] - { - #[serde_as] - #[derive(Serialize, Deserialize)] - struct A { - #[serde_as(as = "object_id::FromHexString")] - oid: String, - - #[serde_as(as = "Option")] - oid_optional_none: Option, - - #[serde_as(as = "Option")] - oid_optional_some: Option, - - #[serde_as(as = "Vec")] - oid_vector: Vec, - } - - let oid = ObjectId::new(); - let a = A { - oid: oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(oid.to_string()), - oid_vector: vec![oid.to_string()], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_object_id("oid").unwrap(), - oid, - "Expected serialized oid to match original ObjectId." - ); - - assert_eq!( - doc.get("oid_optional_none"), - Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." - ); + #[serde_as] + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct A { + #[serde_as(as = "object_id::FromHexString")] + oid: String, - match doc.get("oid_optional_some") { - Some(Bson::ObjectId(value)) => { - assert_eq!( - *value, oid, - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON ObjectId.") - } - } + #[serde_as(as = "Option")] + oid_optional_none: Option, - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = a - .oid_vector - .into_iter() - .map(|oid| Bson::ObjectId(ObjectId::parse_str(oid).unwrap())) - .collect(); - assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_vector match the original." - ); + #[serde_as(as = "Option")] + oid_optional_some: Option, - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); + #[serde_as(as = "Vec")] + oid_vector: Vec, + } - // Validate deserialized data - assert_eq!( - a_deserialized.oid, - oid.to_string(), - "Expected deserialized oid to match the original." - ); + let oid = ObjectId::new(); + let a = A { + oid: oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(oid.to_string()), + oid_vector: vec![oid.to_string()], + }; - assert_eq!( - a_deserialized.oid_optional_some, - Some(oid.to_string()), - "Expected deserialized oid_optional_some to match the original." - ); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - assert_eq!( - a_deserialized.oid_vector, - vec![oid.to_string()], - "Expected deserialized oid_vector to match the original." - ); + // Validate serialized data + assert_eq!( + doc.get_object_id("oid").unwrap(), + oid, + "Expected serialized oid to match original ObjectId." + ); - // Validate serializing error case with an invalid ObjectId string - let invalid_oid = "invalid_oid"; - let bad_a = A { - oid: invalid_oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(invalid_oid.to_string()), - oid_vector: vec![invalid_oid.to_string()], - }; - let result = serialize_to_document(&bad_a); - assert!( - result.is_err(), - "Serialization should fail for invalid ObjectId strings" - ); - let err_string = format!("{:?}", result.unwrap_err()); - assert!( - err_string.contains("BSON error"), - "Expected error message to mention BSON error: {}", - err_string - ); + assert_eq!( + doc.get("oid_optional_none"), + Some(&Bson::Null), + "Expected serialized oid_optional_none to be None." + ); - #[serde_as] - #[derive(Serialize, Deserialize, Debug)] - struct B { - #[serde_as(as = "object_id::AsHexString")] - oid: ObjectId, + assert_eq!( + doc.get("oid_optional_some"), + Some(&Bson::ObjectId(oid)), + "Expected serialized oid_optional_some to match original." + ); - #[serde_as(as = "Option")] - oid_optional_none: Option, + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = vec![Bson::ObjectId(oid)]; + assert_eq!( + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_vector match the original." + ); - #[serde_as(as = "Option")] - oid_optional_some: Option, + // 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")] - oid_vector: Vec, - } + // Validate serializing error case with an invalid ObjectId string + let invalid_oid = "invalid_oid"; + let bad_a = A { + oid: invalid_oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(invalid_oid.to_string()), + oid_vector: vec![invalid_oid.to_string()], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Serialization should fail for invalid ObjectId strings" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", + err_string + ); - let oid = ObjectId::new(); - let b = B { - oid, - oid_optional_none: None, - oid_optional_some: Some(oid), - oid_vector: vec![oid], - }; + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "object_id::AsHexString")] + oid: ObjectId, - // Serialize the struct to BSON - let doc = serialize_to_document(&b).unwrap(); + #[serde_as(as = "Option")] + oid_optional_none: Option, - // Validate serialized data - assert_eq!( - doc.get_str("oid").unwrap(), - oid.to_hex(), - "Expected serialized oid to match original ObjectId as hex string." - ); + #[serde_as(as = "Option")] + oid_optional_some: Option, - assert_eq!( - doc.get("oid_optional_none"), - Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." - ); + #[serde_as(as = "Vec")] + oid_vector: Vec, + } - match doc.get("oid_optional_some") { - Some(Bson::String(value)) => { - assert_eq!( - *value, - oid.to_hex(), - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON String.") - } - } + let oid = ObjectId::new(); + let b = B { + oid, + oid_optional_none: None, + oid_optional_some: Some(oid), + oid_vector: vec![oid], + }; - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = b - .oid_vector - .into_iter() - .map(|oid| Bson::String(oid.to_hex())) - .collect(); - assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_vector match the original." - ); + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); + // Validate serialized data + assert_eq!( + doc.get_str("oid").unwrap(), + oid.to_hex(), + "Expected serialized oid to match original ObjectId as hex string." + ); - // Validate deserialized data - assert_eq!( - b_deserialized.oid, oid, - "Expected deserialized oid to match the original." - ); + assert_eq!( + doc.get("oid_optional_none"), + Some(&Bson::Null), + "Expected serialized oid_optional_none to be None." + ); - assert_eq!( - b_deserialized.oid_optional_none, None, - "Expected deserialized oid_optional_none to be None." - ); + assert_eq!( + doc.get("oid_optional_some"), + Some(&Bson::String(oid.to_hex())), + "Expected serialized oid_optional_some to match original." + ); - assert_eq!( - b_deserialized.oid_optional_some, - Some(oid), - "Expected deserialized oid_optional_some to match the original." - ); + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = vec![Bson::String(oid.to_hex())]; + assert_eq!( + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_vector match the original." + ); - assert_eq!( - b_deserialized.oid_vector, - vec![oid], - "Expected deserialized oid_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 ObjectId string - let invalid_doc = doc! { - "oid": "not_a_valid_oid", - "oid_optional_none": Bson::Null, - "oid_optional_some": "also_invalid_oid", - "oid_vector": ["bad1", "bad2"] - }; - let result: Result = deserialize_from_document(invalid_doc); - assert!( - result.is_err(), - "Deserialization should fail for invalid ObjectId strings" - ); - let err_string = format!("{:?}", result.unwrap_err()); - assert!( - err_string.contains("BSON error"), - "Expected error message to mention BSON error: {}", - err_string - ); - } + // Validate deserializing error case with an invalid ObjectId string + let invalid_doc = doc! { + "oid": "not_a_valid_oid", + "oid_optional_none": Bson::Null, + "oid_optional_some": "also_invalid_oid", + "oid_vector": ["bad1", "bad2"] + }; + let result: Result = deserialize_from_document(invalid_doc); + assert!( + result.is_err(), + "Deserialization should fail for invalid ObjectId strings" + ); + let err_string = format!("{:?}", result.unwrap_err()); + assert!( + err_string.contains("BSON error"), + "Expected error message to mention BSON error: {}", + err_string + ); } #[test] @@ -1692,7 +1635,7 @@ fn test_u64_helpers() { #[cfg(feature = "serde_with-3")] { #[serde_as] - #[derive(Deserialize, Serialize, Debug)] + #[derive(Deserialize, Serialize, Debug, PartialEq)] struct A { #[serde_as(as = "u64::AsF64")] pub value: u64, @@ -1747,30 +1690,11 @@ fn test_u64_helpers() { "Expected each serialized element in value_vector to match the original." ); - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - // Validate deserialized data + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - a_deserialized.value, value, - "Expected deserialized value to match the original." - ); - - assert_eq!( - a_deserialized.value_optional_none, None, - "Expected deserialized val_optional_none to be None." - ); - - assert_eq!( - a_deserialized.value_optional_some, - Some(value), - "Expected deserialized val_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.value_vector, - vec![value], - "Expected deserialized val_vector to match the original." + a_deserialized, a, + "Deserialized struct does not match original." ); // Validate serializing error case with u64 over size limit From 3103c27847a3a4e43a429328e35422e448fa0448 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:06:37 -0400 Subject: [PATCH 45/61] Try uncommenting conditional compilation on rustdoc comments --- src/serde_helpers.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 0ac05d81..8311bc44 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -203,8 +203,8 @@ pub mod datetime { #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] /// Converts a [`chrono::DateTime`] to and from a [`DateTime`]. /// ```rust - /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - /// # { + /// #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] + /// { /// use bson::serde_helpers::datetime; /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; @@ -214,7 +214,7 @@ pub mod datetime { /// #[serde_as(as = "datetime::FromChrono04DateTime")] /// pub date: chrono::DateTime, /// } - /// # } + /// } /// ``` pub FromChrono04DateTime, chrono::DateTime, @@ -231,8 +231,8 @@ pub mod datetime { #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] /// Converts a [`time::OffsetDateTime`] to and from a [`DateTime`]. /// ```rust - /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] - /// # { + /// #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] + /// { /// use bson::serde_helpers::datetime; /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; @@ -242,7 +242,7 @@ pub mod datetime { /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] /// pub date: time::OffsetDateTime, /// } - /// # } + /// } /// ``` pub FromTime03OffsetDateTime, time::OffsetDateTime, From 6f74eada6ecd0662c0972cf39005e266b5e76554 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:08:37 -0400 Subject: [PATCH 46/61] Undo uncommenting rustdocs --- src/serde_helpers.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 8311bc44..0ac05d81 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -203,8 +203,8 @@ pub mod datetime { #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] /// Converts a [`chrono::DateTime`] to and from a [`DateTime`]. /// ```rust - /// #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] - /// { + /// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))] + /// # { /// use bson::serde_helpers::datetime; /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; @@ -214,7 +214,7 @@ pub mod datetime { /// #[serde_as(as = "datetime::FromChrono04DateTime")] /// pub date: chrono::DateTime, /// } - /// } + /// # } /// ``` pub FromChrono04DateTime, chrono::DateTime, @@ -231,8 +231,8 @@ pub mod datetime { #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] /// Converts a [`time::OffsetDateTime`] to and from a [`DateTime`]. /// ```rust - /// #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] - /// { + /// # #[cfg(all(feature = "time-0_3", feature = "serde_with-3"))] + /// # { /// use bson::serde_helpers::datetime; /// use serde::{Serialize, Deserialize}; /// use serde_with::serde_as; @@ -242,7 +242,7 @@ pub mod datetime { /// #[serde_as(as = "datetime::FromTime03OffsetDateTime")] /// pub date: time::OffsetDateTime, /// } - /// } + /// # } /// ``` pub FromTime03OffsetDateTime, time::OffsetDateTime, From a3dd1b86d0205eb75eb1e7cf340a77d6d923583b Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:22:41 -0400 Subject: [PATCH 47/61] Fix clippy attempt - move conditional compilation attribute --- src/serde_helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index fcf97f99..ded62da6 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -323,8 +323,8 @@ pub mod datetime { } ); + #[cfg(feature = "chrono-0_4")] serde_conv_doc!( - #[cfg(feature = "chrono-0_4")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))] /// Converts a [`chrono::DateTime`] to and from a [`DateTime`]. /// ```rust @@ -351,8 +351,8 @@ pub mod datetime { } ); + #[cfg(feature = "time-0_3")] serde_conv_doc!( - #[cfg(feature = "time-0_3")] #[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))] /// Converts a [`time::OffsetDateTime`] to and from a [`DateTime`]. /// ```rust From f81dfd23cf152c185fa84ab4e029ba2e29fa97d3 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:30:54 -0400 Subject: [PATCH 48/61] Revert all ObjectId changes (separate to another PR) --- src/serde_helpers.rs | 28 ++- src/tests/serde.rs | 397 +++++++++++++++++++++++++------------------ 2 files changed, 241 insertions(+), 184 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 0ac05d81..b7a1f29e 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -31,25 +31,22 @@ pub use uuid_1_as_python_legacy_binary::{ serialize as serialize_uuid_1_as_python_legacy_binary, }; -/// Type converters for serializing and deserializing [`crate::ObjectId`] using -/// [`serde_with::serde_as`]. ## Available converters -/// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. -/// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. #[cfg(feature = "serde_with-3")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { use crate::{macros::serde_conv_doc, oid::ObjectId}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// Converts an [`ObjectId`] to and from a hex string. + /// Contains functions to serialize an ObjectId as a hex string and deserialize an + /// ObjectId from a hex string /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// # { - /// use bson::{serde_helpers::object_id, oid::ObjectId}; - /// use serde::{Serialize, Deserialize}; - /// use serde_with::serde_as; + /// { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::object_id; + /// # use serde_with::serde_as; + /// # use bson::oid::ObjectId; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -69,13 +66,14 @@ pub mod object_id { ); serde_conv_doc!( - /// Converts a hex string to and from an [`ObjectId`]. + /// Contains functions to serialize a hex string as an ObjectId and deserialize a + /// hex string from an ObjectId /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// # { - /// use bson::serde_helpers::object_id; - /// use serde::{Serialize, Deserialize}; - /// use serde_with::serde_as; + /// { + /// # use serde::{Serialize, Deserialize}; + /// # use bson::serde_helpers::object_id; + /// # use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 527c35de..bf126b42 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -651,175 +651,6 @@ fn test_serialize_deserialize_unsigned_numbers() { assert!(doc_result.is_err()); } -#[test] -#[cfg(feature = "serde_with-3")] -fn test_oid_helpers() { - let _guard = LOCK.run_concurrently(); - - #[serde_as] - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct A { - #[serde_as(as = "object_id::FromHexString")] - oid: String, - - #[serde_as(as = "Option")] - oid_optional_none: Option, - - #[serde_as(as = "Option")] - oid_optional_some: Option, - - #[serde_as(as = "Vec")] - oid_vector: Vec, - } - - let oid = ObjectId::new(); - let a = A { - oid: oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(oid.to_string()), - oid_vector: vec![oid.to_string()], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_object_id("oid").unwrap(), - oid, - "Expected serialized oid to match original ObjectId." - ); - - assert_eq!( - doc.get("oid_optional_none"), - Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." - ); - - assert_eq!( - doc.get("oid_optional_some"), - Some(&Bson::ObjectId(oid)), - "Expected serialized oid_optional_some to match original." - ); - - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = vec![Bson::ObjectId(oid)]; - assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_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." - ); - - // Validate serializing error case with an invalid ObjectId string - let invalid_oid = "invalid_oid"; - let bad_a = A { - oid: invalid_oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(invalid_oid.to_string()), - oid_vector: vec![invalid_oid.to_string()], - }; - let result = serialize_to_document(&bad_a); - assert!( - result.is_err(), - "Serialization should fail for invalid ObjectId 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(Serialize, Deserialize, Debug, PartialEq)] - struct B { - #[serde_as(as = "object_id::AsHexString")] - oid: ObjectId, - - #[serde_as(as = "Option")] - oid_optional_none: Option, - - #[serde_as(as = "Option")] - oid_optional_some: Option, - - #[serde_as(as = "Vec")] - oid_vector: Vec, - } - - let oid = ObjectId::new(); - let b = B { - oid, - oid_optional_none: None, - oid_optional_some: Some(oid), - oid_vector: vec![oid], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&b).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_str("oid").unwrap(), - oid.to_hex(), - "Expected serialized oid to match original ObjectId as hex string." - ); - - assert_eq!( - doc.get("oid_optional_none"), - Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." - ); - - assert_eq!( - doc.get("oid_optional_some"), - Some(&Bson::String(oid.to_hex())), - "Expected serialized oid_optional_some to match original." - ); - - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = vec![Bson::String(oid.to_hex())]; - assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_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 deserializing error case with an invalid ObjectId string - let invalid_doc = doc! { - "oid": "not_a_valid_oid", - "oid_optional_none": Bson::Null, - "oid_optional_some": "also_invalid_oid", - "oid_vector": ["bad1", "bad2"] - }; - let result: Result = deserialize_from_document(invalid_doc); - assert!( - result.is_err(), - "Deserialization should fail for invalid ObjectId 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_rfc3339_string_helpers() { @@ -1926,6 +1757,234 @@ fn test_u64_helpers() { } } +#[test] +fn test_oid_helpers() { + let _guard = LOCK.run_concurrently(); + + #[cfg(feature = "serde_with-3")] + { + #[serde_as] + #[derive(Serialize, Deserialize)] + struct A { + #[serde_as(as = "object_id::FromHexString")] + oid: String, + + #[serde_as(as = "Option")] + oid_optional_none: Option, + + #[serde_as(as = "Option")] + oid_optional_some: Option, + + #[serde_as(as = "Vec")] + oid_vector: Vec, + } + + let oid = ObjectId::new(); + + let a = A { + oid: oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(oid.to_string()), + oid_vector: vec![oid.to_string()], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_object_id("oid").unwrap(), + oid, + "Expected serialized oid to match original ObjectId." + ); + + assert_eq!( + doc.get("oid_optional_none"), + Some(&Bson::Null), + "Expected serialized oid_optional_none to be None." + ); + + match doc.get("oid_optional_some") { + Some(Bson::ObjectId(value)) => { + assert_eq!( + *value, oid, + "Expected serialized oid_optional_some to match original ObjectId." + ) + } + _ => { + panic!("Expected serialized oid_optional_some to be a BSON ObjectId.") + } + } + + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = a + .oid_vector + .into_iter() + .map(|oid| Bson::ObjectId(ObjectId::parse_str(oid).unwrap())) + .collect(); + assert_eq!( + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_vector match the original." + ); + + // Deserialize the BSON back to the struct + let a_deserialized: A = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + a_deserialized.oid, + oid.to_string(), + "Expected deserialized oid to match the original." + ); + + assert_eq!( + a_deserialized.oid_optional_some, + Some(oid.to_string()), + "Expected deserialized oid_optional_some to match the original." + ); + + assert_eq!( + a_deserialized.oid_vector, + vec![oid.to_string()], + "Expected deserialized oid_vector to match the original." + ); + + // Validate serializing error case with an invalid ObjectId string + let invalid_oid = "invalid_oid"; + let bad_a = A { + oid: invalid_oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(invalid_oid.to_string()), + oid_vector: vec![invalid_oid.to_string()], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Deserialization should fail for invalid ObjectId 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(Serialize, Deserialize, Debug)] + struct B { + #[serde_as(as = "object_id::AsHexString")] + oid: ObjectId, + + #[serde_as(as = "Option")] + oid_optional_none: Option, + + #[serde_as(as = "Option")] + oid_optional_some: Option, + + #[serde_as(as = "Vec")] + oid_vector: Vec, + } + + let oid = ObjectId::new(); + let b = B { + oid, + oid_optional_none: None, + oid_optional_some: Some(oid), + oid_vector: vec![oid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_str("oid").unwrap(), + oid.to_hex(), + "Expected serialized oid to match original ObjectId as hex string." + ); + + assert_eq!( + doc.get("oid_optional_none"), + Some(&Bson::Null), + "Expected serialized oid_optional_none to be None." + ); + + match doc.get("oid_optional_some") { + Some(Bson::String(value)) => { + assert_eq!( + *value, + oid.to_hex(), + "Expected serialized oid_optional_some to match original ObjectId." + ) + } + _ => { + panic!("Expected serialized oid_optional_some to be a BSON String.") + } + } + + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = b + .oid_vector + .into_iter() + .map(|oid| Bson::String(oid.to_hex())) + .collect(); + assert_eq!( + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_vector match the original." + ); + + // Deserialize the BSON back to the struct + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + + // Validate deserialized data + assert_eq!( + b_deserialized.oid, oid, + "Expected deserialized oid to match the original." + ); + + assert_eq!( + b_deserialized.oid_optional_none, None, + "Expected deserialized oid_optional_none to be None." + ); + + assert_eq!( + b_deserialized.oid_optional_some, + Some(oid), + "Expected deserialized oid_optional_some to match the original." + ); + + assert_eq!( + b_deserialized.oid_vector, + vec![oid], + "Expected deserialized oid_vector to match the original.." + ); + + // Validate deserializing error case with an invalid ObjectId string + let invalid_doc = doc! { + "oid": "not_a_valid_oid", + "oid_optional_none": Bson::Null, + "oid_optional_some": "also_invalid_oid", + "oid_vector": ["bad1", "bad2"] + }; + let result: Result = deserialize_from_document(invalid_doc); + + assert!( + result.is_err(), + "Deserialization should fail for invalid ObjectId 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 = "uuid-1")] fn test_uuid_1_helpers() { From d3eb97e305d0d4abc431c4f578c56320856d77bd Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:34:17 -0400 Subject: [PATCH 49/61] Revert "Revert all ObjectId changes (separate to another PR)" This reverts commit f81dfd23cf152c185fa84ab4e029ba2e29fa97d3. --- src/serde_helpers.rs | 28 +-- src/tests/serde.rs | 397 ++++++++++++++++++------------------------- 2 files changed, 184 insertions(+), 241 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index b7a1f29e..0ac05d81 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -31,22 +31,25 @@ pub use uuid_1_as_python_legacy_binary::{ serialize as serialize_uuid_1_as_python_legacy_binary, }; +/// Type converters for serializing and deserializing [`crate::ObjectId`] using +/// [`serde_with::serde_as`]. ## Available converters +/// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. +/// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. #[cfg(feature = "serde_with-3")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { use crate::{macros::serde_conv_doc, oid::ObjectId}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; serde_conv_doc!( - /// Contains functions to serialize an ObjectId as a hex string and deserialize an - /// ObjectId from a hex string + /// Converts an [`ObjectId`] to and from a hex string. /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::object_id; - /// # use serde_with::serde_as; - /// # use bson::oid::ObjectId; + /// # { + /// use bson::{serde_helpers::object_id, oid::ObjectId}; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { @@ -66,14 +69,13 @@ pub mod object_id { ); serde_conv_doc!( - /// Contains functions to serialize a hex string as an ObjectId and deserialize a - /// hex string from an ObjectId + /// Converts a hex string to and from an [`ObjectId`]. /// ```rust /// # #[cfg(feature = "serde_with-3")] - /// { - /// # use serde::{Serialize, Deserialize}; - /// # use bson::serde_helpers::object_id; - /// # use serde_with::serde_as; + /// # { + /// use bson::serde_helpers::object_id; + /// use serde::{Serialize, Deserialize}; + /// use serde_with::serde_as; /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct Item { diff --git a/src/tests/serde.rs b/src/tests/serde.rs index bf126b42..527c35de 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -651,6 +651,175 @@ fn test_serialize_deserialize_unsigned_numbers() { assert!(doc_result.is_err()); } +#[test] +#[cfg(feature = "serde_with-3")] +fn test_oid_helpers() { + let _guard = LOCK.run_concurrently(); + + #[serde_as] + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct A { + #[serde_as(as = "object_id::FromHexString")] + oid: String, + + #[serde_as(as = "Option")] + oid_optional_none: Option, + + #[serde_as(as = "Option")] + oid_optional_some: Option, + + #[serde_as(as = "Vec")] + oid_vector: Vec, + } + + let oid = ObjectId::new(); + let a = A { + oid: oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(oid.to_string()), + oid_vector: vec![oid.to_string()], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_object_id("oid").unwrap(), + oid, + "Expected serialized oid to match original ObjectId." + ); + + assert_eq!( + doc.get("oid_optional_none"), + Some(&Bson::Null), + "Expected serialized oid_optional_none to be None." + ); + + assert_eq!( + doc.get("oid_optional_some"), + Some(&Bson::ObjectId(oid)), + "Expected serialized oid_optional_some to match original." + ); + + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = vec![Bson::ObjectId(oid)]; + assert_eq!( + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_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." + ); + + // Validate serializing error case with an invalid ObjectId string + let invalid_oid = "invalid_oid"; + let bad_a = A { + oid: invalid_oid.to_string(), + oid_optional_none: None, + oid_optional_some: Some(invalid_oid.to_string()), + oid_vector: vec![invalid_oid.to_string()], + }; + let result = serialize_to_document(&bad_a); + assert!( + result.is_err(), + "Serialization should fail for invalid ObjectId 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(Serialize, Deserialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "object_id::AsHexString")] + oid: ObjectId, + + #[serde_as(as = "Option")] + oid_optional_none: Option, + + #[serde_as(as = "Option")] + oid_optional_some: Option, + + #[serde_as(as = "Vec")] + oid_vector: Vec, + } + + let oid = ObjectId::new(); + let b = B { + oid, + oid_optional_none: None, + oid_optional_some: Some(oid), + oid_vector: vec![oid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); + + // Validate serialized data + assert_eq!( + doc.get_str("oid").unwrap(), + oid.to_hex(), + "Expected serialized oid to match original ObjectId as hex string." + ); + + assert_eq!( + doc.get("oid_optional_none"), + Some(&Bson::Null), + "Expected serialized oid_optional_none to be None." + ); + + assert_eq!( + doc.get("oid_optional_some"), + Some(&Bson::String(oid.to_hex())), + "Expected serialized oid_optional_some to match original." + ); + + let oid_vector = doc + .get_array("oid_vector") + .expect("Expected serialized oid_vector to be a BSON array."); + let expected_oid_vector: Vec = vec![Bson::String(oid.to_hex())]; + assert_eq!( + oid_vector, &expected_oid_vector, + "Expected each serialized element in oid_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 deserializing error case with an invalid ObjectId string + let invalid_doc = doc! { + "oid": "not_a_valid_oid", + "oid_optional_none": Bson::Null, + "oid_optional_some": "also_invalid_oid", + "oid_vector": ["bad1", "bad2"] + }; + let result: Result = deserialize_from_document(invalid_doc); + assert!( + result.is_err(), + "Deserialization should fail for invalid ObjectId 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_rfc3339_string_helpers() { @@ -1757,234 +1926,6 @@ fn test_u64_helpers() { } } -#[test] -fn test_oid_helpers() { - let _guard = LOCK.run_concurrently(); - - #[cfg(feature = "serde_with-3")] - { - #[serde_as] - #[derive(Serialize, Deserialize)] - struct A { - #[serde_as(as = "object_id::FromHexString")] - oid: String, - - #[serde_as(as = "Option")] - oid_optional_none: Option, - - #[serde_as(as = "Option")] - oid_optional_some: Option, - - #[serde_as(as = "Vec")] - oid_vector: Vec, - } - - let oid = ObjectId::new(); - - let a = A { - oid: oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(oid.to_string()), - oid_vector: vec![oid.to_string()], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&a).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_object_id("oid").unwrap(), - oid, - "Expected serialized oid to match original ObjectId." - ); - - assert_eq!( - doc.get("oid_optional_none"), - Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." - ); - - match doc.get("oid_optional_some") { - Some(Bson::ObjectId(value)) => { - assert_eq!( - *value, oid, - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON ObjectId.") - } - } - - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = a - .oid_vector - .into_iter() - .map(|oid| Bson::ObjectId(ObjectId::parse_str(oid).unwrap())) - .collect(); - assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_vector match the original." - ); - - // Deserialize the BSON back to the struct - let a_deserialized: A = deserialize_from_document(doc).unwrap(); - - // Validate deserialized data - assert_eq!( - a_deserialized.oid, - oid.to_string(), - "Expected deserialized oid to match the original." - ); - - assert_eq!( - a_deserialized.oid_optional_some, - Some(oid.to_string()), - "Expected deserialized oid_optional_some to match the original." - ); - - assert_eq!( - a_deserialized.oid_vector, - vec![oid.to_string()], - "Expected deserialized oid_vector to match the original." - ); - - // Validate serializing error case with an invalid ObjectId string - let invalid_oid = "invalid_oid"; - let bad_a = A { - oid: invalid_oid.to_string(), - oid_optional_none: None, - oid_optional_some: Some(invalid_oid.to_string()), - oid_vector: vec![invalid_oid.to_string()], - }; - let result = serialize_to_document(&bad_a); - assert!( - result.is_err(), - "Deserialization should fail for invalid ObjectId 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(Serialize, Deserialize, Debug)] - struct B { - #[serde_as(as = "object_id::AsHexString")] - oid: ObjectId, - - #[serde_as(as = "Option")] - oid_optional_none: Option, - - #[serde_as(as = "Option")] - oid_optional_some: Option, - - #[serde_as(as = "Vec")] - oid_vector: Vec, - } - - let oid = ObjectId::new(); - let b = B { - oid, - oid_optional_none: None, - oid_optional_some: Some(oid), - oid_vector: vec![oid], - }; - - // Serialize the struct to BSON - let doc = serialize_to_document(&b).unwrap(); - - // Validate serialized data - assert_eq!( - doc.get_str("oid").unwrap(), - oid.to_hex(), - "Expected serialized oid to match original ObjectId as hex string." - ); - - assert_eq!( - doc.get("oid_optional_none"), - Some(&Bson::Null), - "Expected serialized oid_optional_none to be None." - ); - - match doc.get("oid_optional_some") { - Some(Bson::String(value)) => { - assert_eq!( - *value, - oid.to_hex(), - "Expected serialized oid_optional_some to match original ObjectId." - ) - } - _ => { - panic!("Expected serialized oid_optional_some to be a BSON String.") - } - } - - let oid_vector = doc - .get_array("oid_vector") - .expect("Expected serialized oid_vector to be a BSON array."); - let expected_oid_vector: Vec = b - .oid_vector - .into_iter() - .map(|oid| Bson::String(oid.to_hex())) - .collect(); - assert_eq!( - oid_vector, &expected_oid_vector, - "Expected each serialized element in oid_vector match the original." - ); - - // Deserialize the BSON back to the struct - let b_deserialized: B = deserialize_from_document(doc).unwrap(); - - // Validate deserialized data - assert_eq!( - b_deserialized.oid, oid, - "Expected deserialized oid to match the original." - ); - - assert_eq!( - b_deserialized.oid_optional_none, None, - "Expected deserialized oid_optional_none to be None." - ); - - assert_eq!( - b_deserialized.oid_optional_some, - Some(oid), - "Expected deserialized oid_optional_some to match the original." - ); - - assert_eq!( - b_deserialized.oid_vector, - vec![oid], - "Expected deserialized oid_vector to match the original.." - ); - - // Validate deserializing error case with an invalid ObjectId string - let invalid_doc = doc! { - "oid": "not_a_valid_oid", - "oid_optional_none": Bson::Null, - "oid_optional_some": "also_invalid_oid", - "oid_vector": ["bad1", "bad2"] - }; - let result: Result = deserialize_from_document(invalid_doc); - - assert!( - result.is_err(), - "Deserialization should fail for invalid ObjectId 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 = "uuid-1")] fn test_uuid_1_helpers() { From 60b6c4f3c74d070a0e6c5d83e98bdb4181a71e03 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:49:21 -0400 Subject: [PATCH 50/61] Move #[cfg(feature = serde_with-3)] attribute to test function --- src/tests/serde.rs | 1231 ++++++++++++++++++++++---------------------- 1 file changed, 613 insertions(+), 618 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index bf126b42..fba3886b 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1032,729 +1032,724 @@ fn test_datetime_time03_offset_datetime_helper() { } #[test] +#[cfg(feature = "serde_with-3")] fn test_u32_helpers() { let _guard = LOCK.run_concurrently(); - #[cfg(feature = "serde_with-3")] - { - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "u32::FromTimestamp")] - pub timestamp: Timestamp, + #[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_none: Option, - #[serde_as(as = "Option")] - pub timestamp_optional_some: Option, + #[serde_as(as = "Option")] + pub timestamp_optional_some: Option, - #[serde_as(as = "Vec")] - pub timestamp_vector: Vec, - } + #[serde_as(as = "Vec")] + pub timestamp_vector: Vec, + } - let time = 12345; - let timestamp = Timestamp { time, increment: 0 }; - let a = A { - timestamp, - timestamp_optional_none: None, - timestamp_optional_some: Some(timestamp), - timestamp_vector: vec![timestamp], - }; + let time = 12345; + let timestamp = Timestamp { time, increment: 0 }; + let a = A { + timestamp, + timestamp_optional_none: None, + timestamp_optional_some: Some(timestamp), + timestamp_vector: vec![timestamp], + }; - // 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("timestamp").unwrap(), - &Bson::Int64(time as i64), - "Expected serialized time to match the original." - ); + // 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_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." - ); + 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." - ); + 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 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 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 - ); + // Validate serializing error case with an invalid Timestamp + let invalid_timestamp_for_serializing = Timestamp { + time: 0, + increment: 2, + }; + 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 + ); - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct B { - #[serde_as(as = "u32::AsTimestamp")] - pub time: u32, + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "u32::AsTimestamp")] + pub time: u32, - #[serde_as(as = "Option")] - pub time_optional_none: Option, + #[serde_as(as = "Option")] + pub time_optional_none: Option, - #[serde_as(as = "Option")] - pub time_optional_some: Option, + #[serde_as(as = "Option")] + pub time_optional_some: Option, - #[serde_as(as = "Vec")] - pub time_vector: Vec, - } + #[serde_as(as = "Vec")] + pub time_vector: Vec, + } - let time = 12345; - let b = B { - time, - time_optional_none: None, - time_optional_some: Some(time), - time_vector: vec![time], - }; + let time = 12345; + let b = B { + 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(); + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); - // Validate serialized data - assert_eq!( - doc.get_timestamp("time").unwrap(), - Timestamp { time, increment: 0 }, - "Expected serialized time to match the original." - ); + // 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_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." - ); + 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." - ); + 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 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 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 - ); + // Validate deserializing error case with an invalid Timestamp + let invalid_timestamp_for_deserializing = Timestamp { + time: 0, + increment: 2, + }; + 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 + ); - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct C { - #[serde_as(as = "u32::AsF64")] - pub value: u32, + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct C { + #[serde_as(as = "u32::AsF64")] + pub value: u32, - #[serde_as(as = "Option")] - pub value_optional_none: Option, + #[serde_as(as = "Option")] + pub value_optional_none: Option, - #[serde_as(as = "Option")] - pub value_optional_some: Option, + #[serde_as(as = "Option")] + pub value_optional_some: Option, - #[serde_as(as = "Vec")] - pub value_vector: Vec, - } + #[serde_as(as = "Vec")] + pub value_vector: Vec, + } - let value = 12345; - let c = C { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; + let value = 12345; + let c = C { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; - // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); - // Validate serialized data - assert_eq!( - doc.get("value"), - Some(&Bson::Double(value as f64)), - "Expected serialized value to match the original." - ); + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); - assert_eq!( - doc.get("value_optional_none"), - Some(&Bson::Null), - "Expected serialized value_optional_none to be None." - ); + 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." - ); + 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." - ); + 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 c_deserialized: C = deserialize_from_document(doc).unwrap(); - assert_eq!( - c_deserialized, c, - "Deserialized struct does not match original." - ); + // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); + assert_eq!( + c_deserialized, c, + "Deserialized struct does not match original." + ); - #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct D { - #[serde_as(as = "u32::AsI32")] - value: u32, + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct D { + #[serde_as(as = "u32::AsI32")] + value: u32, - #[serde_as(as = "Option")] - value_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - value_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - value_vector: Vec, - } + #[serde_as(as = "Vec")] + value_vector: Vec, + } - let value = 1; - let d = D { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; + let value = 1; + let d = D { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; - // Serialize the struct to BSON - let doc = serialize_to_document(&d).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&d).unwrap(); - // Validate serialized data - assert_eq!( - doc.get_i32("value").unwrap(), - value as i32, - "Expected serialized value to match original." - ); + // Validate serialized data + assert_eq!( + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." + ); - assert_eq!( - doc.get("value_optional_none"), - Some(&Bson::Null), - "Expected serialized value_optional_none to be None." - ); + 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::Int32(value as i32)), - "Expected serialized value_optional_some to match original." - ); + assert_eq!( + doc.get("value_optional_some"), + Some(&Bson::Int32(value as i32)), + "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::Int32(value as i32)]; - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the 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::Int32(value as i32)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); - // Validate deserialized data - let d_deserialized: D = deserialize_from_document(doc).unwrap(); - assert_eq!( - d_deserialized, d, - "Deserialized struct does not match original." - ); + // Validate deserialized data + let d_deserialized: D = deserialize_from_document(doc).unwrap(); + assert_eq!( + d_deserialized, d, + "Deserialized struct does not match original." + ); - // Validate serialization fails because u32::MAX is too large to fit in i32 - let invalid_value_for_serializing = u32::MAX; - let bad_d: D = D { - 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_d); - assert!( - result.is_err(), - "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("Cannot convert u32"), - "Expected error message to mention failed u32 to i32 conversion, got: {}", - err_string - ); + // Validate serialization fails because u32::MAX is too large to fit in i32 + let invalid_value_for_serializing = u32::MAX; + let bad_d: D = D { + 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_d); + assert!( + result.is_err(), + "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("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_d = 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_d); - 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 - ); + // Validate deserialization fails for i32::MIN because negative values can't be converted to + // u32 + let invalid_value_for_deserializing = i32::MIN; + let bad_d = 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_d); + 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 + ); - #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct E { - #[serde_as(as = "u32::AsI64")] - value: u32, + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct E { + #[serde_as(as = "u32::AsI64")] + value: u32, - #[serde_as(as = "Option")] - value_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - value_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - value_vector: Vec, - } + #[serde_as(as = "Vec")] + value_vector: Vec, + } - let value = u32::MAX; - let e = E { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; + let value = u32::MAX; + let e = E { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; - // Serialize the struct to BSON - let doc = serialize_to_document(&e).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&e).unwrap(); - // Validate serialized data - assert_eq!( - doc.get_i64("value").unwrap(), - value as i64, - "Expected serialized value to match original." - ); + // Validate serialized data + assert_eq!( + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." + ); - assert_eq!( - doc.get("value_optional_none"), - Some(&Bson::Null), - "Expected serialized value_optional_none to be None." - ); + 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::Int64(value as i64)), - "Expected serialized value_optional_some to match original." - ); + assert_eq!( + doc.get("value_optional_some"), + Some(&Bson::Int64(value as i64)), + "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::Int64(value as i64)]; - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the 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::Int64(value as i64)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); - // Validate deserialized data - let e_deserialized: E = deserialize_from_document(doc).unwrap(); - assert_eq!( - e_deserialized, e, - "Round-trip failed: deserialized struct did not match original." - ); + // Validate deserialized data + let e_deserialized: E = deserialize_from_document(doc).unwrap(); + assert_eq!( + e_deserialized, e, + "Round-trip failed: deserialized struct did not match original." + ); - // Validate deserialization fails for i64::MIN because negative values can't be converted to - // u32 - let invalid_value_for_deserializing = i64::MIN; - let bad_e = 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_e); - assert!( - result.is_err(), - "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("Cannot convert i64"), - "Expected error message to mention failed i64 to u32 conversion, got: {}", - err_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_e = 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_e); + assert!( + result.is_err(), + "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("Cannot convert i64"), + "Expected error message to mention failed i64 to u32 conversion, got: {}", + err_string + ); } #[test] +#[cfg(feature = "serde_with-3")] fn test_u64_helpers() { let _guard = LOCK.run_concurrently(); - #[cfg(feature = "serde_with-3")] - { - #[serde_as] - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde_as(as = "u64::AsF64")] - pub value: u64, + #[serde_as] + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "u64::AsF64")] + pub value: u64, - #[serde_as(as = "Option")] - pub value_optional_none: Option, + #[serde_as(as = "Option")] + pub value_optional_none: Option, - #[serde_as(as = "Option")] - pub value_optional_some: Option, + #[serde_as(as = "Option")] + pub value_optional_some: Option, - #[serde_as(as = "Vec")] - pub value_vector: Vec, - } + #[serde_as(as = "Vec")] + pub value_vector: Vec, + } - let value = 12345; - let a = A { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; + let value = 12345; + 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(&a).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); - // Validate serialized data - assert_eq!( - doc.get("value"), - Some(&Bson::Double(value as f64)), - "Expected serialized value to match the original." - ); + // Validate serialized data + assert_eq!( + doc.get("value"), + Some(&Bson::Double(value as f64)), + "Expected serialized value to match the original." + ); - assert_eq!( - doc.get("value_optional_none"), - Some(&Bson::Null), - "Expected serialized value_optional_none to be None." - ); + 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." - ); + 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)]; - // TODO: check whether this can be applied to other converters - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the 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)]; + // TODO: check whether this can be applied to other converters + 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." - ); + // 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 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 - ); + // 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 + ); - #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct B { - #[serde_as(as = "u64::AsI32")] - value: u64, + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct B { + #[serde_as(as = "u64::AsI32")] + value: u64, - #[serde_as(as = "Option")] - value_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - value_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - value_vector: Vec, - } + #[serde_as(as = "Vec")] + value_vector: Vec, + } - let value = 1; - let b = B { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; + let value = 1; + let b = B { + 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(); + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); - // Validate serialized data - assert_eq!( - doc.get_i32("value").unwrap(), - value as i32, - "Expected serialized value to match original." - ); + // Validate serialized data + assert_eq!( + doc.get_i32("value").unwrap(), + value as i32, + "Expected serialized value to match original." + ); - assert_eq!( - doc.get("value_optional_none"), - Some(&Bson::Null), - "Expected serialized value_optional_none to be None." - ); + 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::Int32(value as i32)), - "Expected serialized value_optional_some to match original." - ); + assert_eq!( + doc.get("value_optional_some"), + Some(&Bson::Int32(value as i32)), + "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::Int32(value as i32)]; - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the 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::Int32(value as i32)]; + assert_eq!( + 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(); - assert_eq!( - b_deserialized, b, - "Round-trip failed: deserialized struct did not match original." - ); + // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "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_b: B = B { - 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_b); - 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 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_b: B = B { + 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_b); + 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_b = 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_b); - 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 - ); + // Validate deserialization fails for i32::MIN because negative values can't be converted to + // u64 + let invalid_value_for_deserializing = i32::MIN; + let bad_b = 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_b); + 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 + ); - #[serde_as] - #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct C { - #[serde_as(as = "u64::AsI64")] - value: u64, + #[serde_as] + #[derive(Deserialize, Serialize, PartialEq, Debug)] + struct C { + #[serde_as(as = "u64::AsI64")] + value: u64, - #[serde_as(as = "Option")] - value_optional_none: Option, + #[serde_as(as = "Option")] + value_optional_none: Option, - #[serde_as(as = "Option")] - value_optional_some: Option, + #[serde_as(as = "Option")] + value_optional_some: Option, - #[serde_as(as = "Vec")] - value_vector: Vec, - } + #[serde_as(as = "Vec")] + value_vector: Vec, + } - let value = i64::MAX as u64; - let c = C { - value, - value_optional_none: None, - value_optional_some: Some(value), - value_vector: vec![value], - }; + let value = i64::MAX as u64; + let c = C { + value, + value_optional_none: None, + value_optional_some: Some(value), + value_vector: vec![value], + }; - // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); - // Validate serialized data - assert_eq!( - doc.get_i64("value").unwrap(), - value as i64, - "Expected serialized value to match original." - ); + // Validate serialized data + assert_eq!( + doc.get_i64("value").unwrap(), + value as i64, + "Expected serialized value to match original." + ); - assert_eq!( - doc.get("value_optional_none"), - Some(&Bson::Null), - "Expected serialized value_optional_none to be None." - ); + 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::Int64(value as i64)), - "Expected serialized value_optional_some to match original." - ); + assert_eq!( + doc.get("value_optional_some"), + Some(&Bson::Int64(value as i64)), + "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::Int64(value as i64)]; - assert_eq!( - value_vector, &expected_value_vector, - "Expected each serialized element in value_vector to match the 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::Int64(value as i64)]; + assert_eq!( + value_vector, &expected_value_vector, + "Expected each serialized element in value_vector to match the original." + ); - // Validate deserialized data - let c_deserialized: C = deserialize_from_document(doc).unwrap(); - assert_eq!( - c_deserialized, c, - "Round-trip failed: deserialized struct did not match original." - ); + // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); + assert_eq!( + c_deserialized, c, + "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_c: C = C { - 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_c); - 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 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_c: C = C { + 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_c); + 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_c = 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_c); - 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 - ); - } + // Validate deserialization fails for i64::MIN because negative values can't be converted to + // u64 + let invalid_value_for_deserializing = i64::MIN; + let bad_c = 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_c); + 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 + ); } #[test] From d91ba764c1e9db762a3043f2d111bfd0949b39f5 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:55:56 -0400 Subject: [PATCH 51/61] Break tests into smaller functions --- src/tests/serde.rs | 111 ++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index fba3886b..004138d3 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1033,7 +1033,7 @@ fn test_datetime_time03_offset_datetime_helper() { #[test] #[cfg(feature = "serde_with-3")] -fn test_u32_helpers() { +fn test_u32_timestamp_helpers() { let _guard = LOCK.run_concurrently(); #[serde_as] @@ -1206,10 +1206,16 @@ fn test_u32_helpers() { "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 C { + struct A { #[serde_as(as = "u32::AsF64")] pub value: u32, @@ -1224,7 +1230,7 @@ fn test_u32_helpers() { } let value = 12345; - let c = C { + let a = A { value, value_optional_none: None, value_optional_some: Some(value), @@ -1232,7 +1238,7 @@ fn test_u32_helpers() { }; // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); + let doc = serialize_to_document(&a).unwrap(); // Validate serialized data assert_eq!( @@ -1263,15 +1269,21 @@ fn test_u32_helpers() { ); // Validate deserialized data - let c_deserialized: C = deserialize_from_document(doc).unwrap(); + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized, c, + a_deserialized, a, "Deserialized struct does not match original." ); +} + +#[test] +#[cfg(feature = "serde_with-3")] +fn test_u32_i32_helper() { + let _guard = LOCK.run_concurrently(); #[serde_as] #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct D { + struct A { #[serde_as(as = "u32::AsI32")] value: u32, @@ -1286,7 +1298,7 @@ fn test_u32_helpers() { } let value = 1; - let d = D { + let a = A { value, value_optional_none: None, value_optional_some: Some(value), @@ -1294,7 +1306,7 @@ fn test_u32_helpers() { }; // Serialize the struct to BSON - let doc = serialize_to_document(&d).unwrap(); + let doc = serialize_to_document(&a).unwrap(); // Validate serialized data assert_eq!( @@ -1325,21 +1337,21 @@ fn test_u32_helpers() { ); // Validate deserialized data - let d_deserialized: D = deserialize_from_document(doc).unwrap(); + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - d_deserialized, d, + a_deserialized, a, "Deserialized struct does not match original." ); // Validate serialization fails because u32::MAX is too large to fit in i32 let invalid_value_for_serializing = u32::MAX; - let bad_d: D = D { + 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_d); + let result = serialize_to_document(&bad_a); assert!( result.is_err(), "Serialization should fail for u32::MAX since it can't be exactly represented as i32" @@ -1354,13 +1366,13 @@ fn test_u32_helpers() { // Validate deserialization fails for i32::MIN because negative values can't be converted to // u32 let invalid_value_for_deserializing = i32::MIN; - let bad_d = doc! { + 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_d); + 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" @@ -1371,10 +1383,16 @@ fn test_u32_helpers() { "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, PartialEq, Debug)] - struct E { + struct A { #[serde_as(as = "u32::AsI64")] value: u32, @@ -1389,7 +1407,7 @@ fn test_u32_helpers() { } let value = u32::MAX; - let e = E { + let a = A { value, value_optional_none: None, value_optional_some: Some(value), @@ -1397,7 +1415,7 @@ fn test_u32_helpers() { }; // Serialize the struct to BSON - let doc = serialize_to_document(&e).unwrap(); + let doc = serialize_to_document(&a).unwrap(); // Validate serialized data assert_eq!( @@ -1428,22 +1446,22 @@ fn test_u32_helpers() { ); // Validate deserialized data - let e_deserialized: E = deserialize_from_document(doc).unwrap(); + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - e_deserialized, e, + a_deserialized, a, "Round-trip failed: deserialized struct did not match original." ); // Validate deserialization fails for i64::MIN because negative values can't be converted to // u32 let invalid_value_for_deserializing = i64::MIN; - let bad_e = doc! { + 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_e); + 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 u32" @@ -1458,7 +1476,7 @@ fn test_u32_helpers() { #[test] #[cfg(feature = "serde_with-3")] -fn test_u64_helpers() { +fn test_u64_f64_helper() { let _guard = LOCK.run_concurrently(); #[serde_as] @@ -1543,10 +1561,16 @@ fn test_u64_helpers() { "Expected error message to mention failed u64 to f64 conversion, got: {}", err_string ); +} + +#[test] +#[cfg(feature = "serde_with-3")] +fn test_u64_i32_helper() { + let _guard = LOCK.run_concurrently(); #[serde_as] #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct B { + struct A { #[serde_as(as = "u64::AsI32")] value: u64, @@ -1561,7 +1585,7 @@ fn test_u64_helpers() { } let value = 1; - let b = B { + let a = A { value, value_optional_none: None, value_optional_some: Some(value), @@ -1569,7 +1593,7 @@ fn test_u64_helpers() { }; // Serialize the struct to BSON - let doc = serialize_to_document(&b).unwrap(); + let doc = serialize_to_document(&a).unwrap(); // Validate serialized data assert_eq!( @@ -1600,21 +1624,21 @@ fn test_u64_helpers() { ); // 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, + a_deserialized, a, "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_b: B = B { + 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_b); + 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" @@ -1629,13 +1653,13 @@ fn test_u64_helpers() { // Validate deserialization fails for i32::MIN because negative values can't be converted to // u64 let invalid_value_for_deserializing = i32::MIN; - let bad_b = doc! { + 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_b); + 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" @@ -1646,10 +1670,15 @@ fn test_u64_helpers() { "Expected error message to mention failed i32 to u64 conversion, got: {}", err_string ); +} +#[test] +#[cfg(feature = "serde_with-3")] +fn test_u64_i64_helper() { + let _guard = LOCK.run_concurrently(); #[serde_as] #[derive(Deserialize, Serialize, PartialEq, Debug)] - struct C { + struct A { #[serde_as(as = "u64::AsI64")] value: u64, @@ -1664,7 +1693,7 @@ fn test_u64_helpers() { } let value = i64::MAX as u64; - let c = C { + let a = A { value, value_optional_none: None, value_optional_some: Some(value), @@ -1672,7 +1701,7 @@ fn test_u64_helpers() { }; // Serialize the struct to BSON - let doc = serialize_to_document(&c).unwrap(); + let doc = serialize_to_document(&a).unwrap(); // Validate serialized data assert_eq!( @@ -1703,21 +1732,21 @@ fn test_u64_helpers() { ); // Validate deserialized data - let c_deserialized: C = deserialize_from_document(doc).unwrap(); + let a_deserialized: A = deserialize_from_document(doc).unwrap(); assert_eq!( - c_deserialized, c, + a_deserialized, a, "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_c: C = C { + 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_c); + 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 \ @@ -1733,13 +1762,13 @@ fn test_u64_helpers() { // Validate deserialization fails for i64::MIN because negative values can't be converted to // u64 let invalid_value_for_deserializing = i64::MIN; - let bad_c = doc! { + 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_c); + 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" From de0b400a08854b181e6e3b3b3d777b42fae16e0f Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Mon, 14 Jul 2025 23:58:38 -0400 Subject: [PATCH 52/61] Remove whitespace diffs --- src/tests/serde.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 004138d3..e968eba4 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1804,7 +1804,6 @@ fn test_oid_helpers() { } let oid = ObjectId::new(); - let a = A { oid: oid.to_string(), oid_optional_none: None, @@ -1995,7 +1994,6 @@ fn test_oid_helpers() { "oid_vector": ["bad1", "bad2"] }; let result: Result = deserialize_from_document(invalid_doc); - assert!( result.is_err(), "Deserialization should fail for invalid ObjectId strings" @@ -2018,7 +2016,6 @@ fn test_uuid_1_helpers() { let _guard = LOCK.run_concurrently(); #[derive(Serialize, Deserialize)] - struct A { #[serde(with = "uuid_1_as_binary")] uuid: Uuid, @@ -2026,7 +2023,6 @@ fn test_uuid_1_helpers() { let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); let a = A { uuid }; - let doc = serialize_to_document(&a).unwrap(); match doc.get("uuid").unwrap() { Bson::Binary(bin) => { From 6d4951c85fc6eba220e599a6a9eb2bbc8a49528c Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 15 Jul 2025 00:16:57 -0400 Subject: [PATCH 53/61] Fix whitespace --- src/serde_helpers.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index eb75a165..cbaeff1f 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -32,7 +32,8 @@ pub use uuid_1_as_python_legacy_binary::{ }; /// Type converters for serializing and deserializing [`crate::ObjectId`] using -/// [`serde_with::serde_as`]. ## Available converters +/// [`serde_with::serde_as`]. +/// ## Available converters /// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. /// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. #[cfg(feature = "serde_with-3")] From 5ca198f967ccfd984f9361917ad22bea2ae2a835 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 15 Jul 2025 00:24:01 -0400 Subject: [PATCH 54/61] Fix rustdocs whitespace --- src/serde_helpers.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index cbaeff1f..dfda8f2d 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -33,6 +33,7 @@ pub use uuid_1_as_python_legacy_binary::{ /// Type converters for serializing and deserializing [`crate::ObjectId`] using /// [`serde_with::serde_as`]. +/// /// ## Available converters /// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. /// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. @@ -98,6 +99,7 @@ pub mod object_id { /// Type converters for serializing and deserializing [`crate::DateTime`] using /// [`serde_with::serde_as`]. +/// /// ## Available converters /// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 string. /// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a @@ -257,6 +259,7 @@ 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`]. @@ -427,6 +430,7 @@ pub mod u32 { } /// 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`. From 29b289a0077c497e462f99559298f2c118b6a0ce Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 15 Jul 2025 00:25:56 -0400 Subject: [PATCH 55/61] Fix rustdocs whitespace and imports --- src/serde_helpers.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 6958aa94..062ef02f 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -6,10 +6,12 @@ use std::{ result::Result, }; -/// Type converters for serializing and deserializing [`ObjectId`] using [`serde_with::serde_as`]. +/// Type converters for serializing and deserializing [`crate::oid::ObjectId`] using +/// [`serde_with::serde_as`]. +/// /// ## Available converters -/// - [`object_id::AsHexString`] — serializes an [`ObjectId`] as a hex string. -/// - [`object_id::FromHexString`] — serializes a hex string as an [`ObjectId`]. +/// - [`object_id::AsHexString`] — serializes an [`crate::oid::ObjectId`] as a hex string. +/// - [`object_id::FromHexString`] — serializes a hex string as an [`crate::oid::ObjectId`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -72,6 +74,7 @@ pub mod object_id { /// Type converters for serializing and deserializing [`crate::DateTime`] using /// [`serde_with::serde_as`]. +/// /// ## Available converters /// - [`datetime::AsRfc3339String`] — converts a [`crate::DateTime`] to and from an RFC 3339 string. /// - [`datetime::FromRfc3339String`] — converts a RFC 3339 string to and from a @@ -232,6 +235,7 @@ 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`]. @@ -401,6 +405,7 @@ pub mod u32 { } /// 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`. @@ -506,16 +511,17 @@ pub mod u64 { ); } -/// Type converters for serializing and deserializing [`Uuid`] using [`serde_with::serde_as`]. +/// Type converters for serializing and deserializing [`crate::Uuid`] using +/// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`uuid_1::AsBinary`] — serializes a [`Uuid`] as a [`Binary`]. -/// - [`uuid_1::AsCSharpLegacyBinary`] — serializes a [`Uuid`] as a [`Binary`] in the legacy C# -/// driver UUID format. -/// - [`uuid_1::AsJavaLegacyBinary`] — serializes a [`Uuid`] as a [`Binary`] in the legacy Java -/// driver UUID format. -/// - [`uuid_1::AsPythonLegacyBinary`] — serializes a [`Uuid`] as a [`Binary`] in the legacy Python -/// driver UUID format. +/// - [`uuid_1::AsBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`]. +/// - [`uuid_1::AsCSharpLegacyBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`] in the +/// legacy C# driver UUID format. +/// - [`uuid_1::AsJavaLegacyBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`] in the +/// legacy Java driver UUID format. +/// - [`uuid_1::AsPythonLegacyBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`] in the +/// legacy Python driver UUID format. #[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "serde_with-3", feature = "uuid-1"))))] pub mod uuid_1 { From 25324cadcd31bdec6b2fc454b16f9ed39c594765 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 15 Jul 2025 09:36:10 -0400 Subject: [PATCH 56/61] Update rustdocs --- src/serde_helpers.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 062ef02f..7d940d4b 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -10,8 +10,8 @@ use std::{ /// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`object_id::AsHexString`] — serializes an [`crate::oid::ObjectId`] as a hex string. -/// - [`object_id::FromHexString`] — serializes a hex string as an [`crate::oid::ObjectId`]. +/// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. +/// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { @@ -318,7 +318,7 @@ pub mod u32 { serde_conv_doc!( /// Converts a `u32` to and from an `f64`. /// - /// Errors if an exact conversion is not possible. + /// Deserialization errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { @@ -378,7 +378,7 @@ pub mod u32 { serde_conv_doc!( /// Converts a `u32` to and from an `i64`. /// - /// Errors if an exact conversion is not possible. + /// Deserialization errors if an exact conversion is not possible. /// ```rust /// # #[cfg(feature = "serde_with-3")] /// # { From fbe3bfc04a2f1994421ba5e975983acc9cc53478 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 15 Jul 2025 13:56:43 -0400 Subject: [PATCH 57/61] Fix rustdoc imports --- src/serde_helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 7d940d4b..0a8f869e 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -10,8 +10,8 @@ use std::{ /// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`object_id::AsHexString`] — converts an [`crate::ObjectId`] to and from a hex string. -/// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::ObjectId`]. +/// - [`object_id::AsHexString`] — converts an [`crate::oid::ObjectId`] to and from a hex string. +/// - [`object_id::FromHexString`] — converts a hex string to and from an [`crate::oid::ObjectId`]. #[cfg(feature = "serde_with-3")] #[cfg_attr(docsrs, doc(cfg(feature = "serde_with-3")))] pub mod object_id { From b0d6844bd3190daab5b9d161d7e8601fab679702 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 22 Jul 2025 09:50:42 -0400 Subject: [PATCH 58/61] Add back missing uuid tests lost in PR split --- src/tests/serde.rs | 301 +++++++++++++++++++++++++++++++++++++++++++++ src/uuid.rs | 4 +- 2 files changed, 304 insertions(+), 1 deletion(-) diff --git a/src/tests/serde.rs b/src/tests/serde.rs index 36cf88a1..08c22421 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -1900,6 +1900,307 @@ fn test_u64_i64_helper() { ); } +#[test] +#[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] +fn test_uuid_1_helpers() { + use crate::uuid::UuidRepresentation; + use serde_helpers::uuid_1; + use uuid::Uuid; + + let _guard = LOCK.run_concurrently(); + + fn assert_binary_match( + actual: &Bson, + uuid: &Uuid, + expected_subtype: BinarySubtype, + uuid_representation: UuidRepresentation, + ) { + match actual { + Bson::Binary(Binary { subtype, bytes }) => { + assert_eq!( + subtype, &expected_subtype, + "Expected subtype {:?}, but got {:?}", + expected_subtype, subtype + ); + let expected_bytes = { + let uuid: crate::Uuid = crate::uuid::Uuid::from(*uuid); + crate::Binary::from_uuid_with_representation(uuid, uuid_representation).bytes + }; + assert_eq!( + bytes, &expected_bytes, + "Serialized binary bytes did not match for representation {:?}", + uuid_representation + ); + } + other => panic!("Expected Bson::Binary, got {:?}", other), + } + } + + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct A { + #[serde_as(as = "uuid_1::AsBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let a: A = A { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&a).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::Uuid, + UuidRepresentation::Standard, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::Uuid, + UuidRepresentation::Standard, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected serialized uuid_vector to be a BSON array."); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::Uuid, + UuidRepresentation::Standard, + ); + + // 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] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct B { + #[serde_as(as = "uuid_1::AsCSharpLegacyBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let b: B = B { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&b).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::CSharpLegacy, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::CSharpLegacy, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected uuid_vector to be a BSON array"); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::CSharpLegacy, + ); + + // Validate deserialized data + let b_deserialized: B = deserialize_from_document(doc).unwrap(); + assert_eq!( + b_deserialized, b, + "Deserialized struct does not match original." + ); + + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct C { + #[serde_as(as = "uuid_1::AsJavaLegacyBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let c: C = C { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&c).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::JavaLegacy, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::JavaLegacy, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected uuid_vector to be a BSON array"); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::JavaLegacy, + ); + + // Validate deserialized data + let c_deserialized: C = deserialize_from_document(doc).unwrap(); + assert_eq!( + c_deserialized, c, + "Deserialized struct does not match original." + ); + + #[serde_as] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct D { + #[serde_as(as = "uuid_1::AsPythonLegacyBinary")] + uuid: Uuid, + + #[serde_as(as = "Option")] + uuid_optional_none: Option, + + #[serde_as(as = "Option")] + uuid_optional_some: Option, + + #[serde_as(as = "Vec")] + uuid_vector: Vec, + } + + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let d: D = D { + uuid, + uuid_optional_none: None, + uuid_optional_some: Some(uuid), + uuid_vector: vec![uuid], + }; + + // Serialize the struct to BSON + let doc = serialize_to_document(&d).unwrap(); + + // Validate serialized data + assert_binary_match( + doc.get("uuid").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::PythonLegacy, + ); + + assert_eq!( + doc.get("uuid_optional_none"), + Some(&Bson::Null), + "Expected serialized uuid_optional_none to be None." + ); + + assert_binary_match( + doc.get("uuid_optional_some").unwrap(), + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::PythonLegacy, + ); + + let uuid_vector = doc + .get_array("uuid_vector") + .expect("Expected uuid_vector to be a BSON array"); + assert_eq!(uuid_vector.len(), 1); + assert_binary_match( + &uuid_vector[0], + &uuid, + BinarySubtype::UuidOld, + UuidRepresentation::PythonLegacy, + ); + + // Validate deserialized data + let d_deserialized: D = deserialize_from_document(doc).unwrap(); + assert_eq!( + d_deserialized, d, + "Deserialized struct does not match original." + ); +} + #[test] fn large_dates() { let _guard = LOCK.run_concurrently(); diff --git a/src/uuid.rs b/src/uuid.rs index 3bf464ed..64cad341 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -22,8 +22,10 @@ //! # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] //! # { //! # use uuid as uuid; -//! use serde::{Serialize, Deserialize}; //! use bson::doc; +//! use serde::{Serialize, Deserialize}; +//! use serde_with::serde_as; +//! //! //! #[serde_as] //! #[derive(Serialize, Deserialize)] From 43f94a1521e3868c8099da2f799dbf3e62f3052d Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 22 Jul 2025 09:54:14 -0400 Subject: [PATCH 59/61] Remove Result imports in tests --- src/serde_helpers.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 35f2864f..9aec87bd 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -244,7 +244,6 @@ pub mod timestamp { use crate::{macros::serde_conv_doc, Timestamp}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Converts a [`Timestamp`] to and from a `u32`. @@ -427,7 +426,6 @@ pub mod u64 { use crate::macros::serde_conv_doc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; - use std::result::Result; serde_conv_doc!( /// Converts a `u64` to and from an `f64`. From b892c34841a495f4272cd22d8990aa2ede9e0797 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Tue, 22 Jul 2025 12:37:40 -0400 Subject: [PATCH 60/61] Fix lint with Binary unused import --- src/serde_helpers.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 9aec87bd..068efcb9 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -535,13 +535,13 @@ pub mod u64 { #[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "serde_with-3", feature = "uuid-1"))))] pub mod uuid_1 { - use crate::{macros::serde_conv_doc, Binary}; + use crate::macros::serde_conv_doc; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; use uuid::Uuid; serde_conv_doc!( - /// Serializes a [`Uuid`] as a [`Binary`] and deserializes a [`Uuid`] from a [`Binary`]. + /// Serializes a [`Uuid`] as a [`crate::Binary`] and deserializes a [`Uuid`] from a [`crate::Binary`]. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { @@ -568,8 +568,8 @@ pub mod uuid_1 { ); serde_conv_doc!( - /// Serializes a [`Uuid`] to a [`Binary`] in the legacy C# driver UUID format and - /// deserializes [`Uuid`] from a [`Binary`] in the legacy C# driver format. + /// Serializes a [`Uuid`] to a [`crate::Binary`] in the legacy C# driver UUID format and + /// deserializes [`Uuid`] from a [`crate::Binary`] in the legacy C# driver format. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { @@ -603,8 +603,8 @@ pub mod uuid_1 { ); serde_conv_doc!( - /// Serializes a [`Uuid`] to a [`Binary`] in the legacy Java driver UUID format and - /// deserializes [`Uuid`] from a [`Binary`] in the legacy Java driver format. + /// Serializes a [`Uuid`] to a [`crate::Binary`] in the legacy Java driver UUID format and + /// deserializes [`Uuid`] from a [`crate::Binary`] in the legacy Java driver format. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { @@ -638,8 +638,8 @@ pub mod uuid_1 { ); serde_conv_doc!( - /// Serializes a [`Uuid`] to a [`Binary`] in the legacy Python driver UUID format and - /// deserializes [`Uuid`] from a [`Binary`] in the legacy Python driver format. + /// Serializes a [`Uuid`] to a [`crate::Binary`] in the legacy Python driver UUID format and + /// deserializes [`Uuid`] from a [`crate::Binary`] in the legacy Python driver format. /// ```rust /// # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))] /// # { From 8b6ec905b4abf5e079c796f30ed2758bb4193b59 Mon Sep 17 00:00:00 2001 From: Jamie Tsai Date: Wed, 23 Jul 2025 11:36:10 -0400 Subject: [PATCH 61/61] Fix rustdoc typo --- src/serde_helpers.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 068efcb9..78c7ad9b 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -521,16 +521,16 @@ pub mod u64 { ); } -/// Type converters for serializing and deserializing [`crate::Uuid`] using +/// Type converters for serializing and deserializing [`uuid::Uuid`] using /// [`serde_with::serde_as`]. /// /// ## Available converters -/// - [`uuid_1::AsBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`]. -/// - [`uuid_1::AsCSharpLegacyBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`] in the +/// - [`uuid_1::AsBinary`] — serializes a [`uuid::Uuid`] as a [`crate::Binary`]. +/// - [`uuid_1::AsCSharpLegacyBinary`] — serializes a [`uuid::Uuid`] as a [`crate::Binary`] in the /// legacy C# driver UUID format. -/// - [`uuid_1::AsJavaLegacyBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`] in the +/// - [`uuid_1::AsJavaLegacyBinary`] — serializes a [`uuid::Uuid`] as a [`crate::Binary`] in the /// legacy Java driver UUID format. -/// - [`uuid_1::AsPythonLegacyBinary`] — serializes a [`crate::Uuid`] as a [`crate::Binary`] in the +/// - [`uuid_1::AsPythonLegacyBinary`] — serializes a [`uuid::Uuid`] as a [`crate::Binary`] in the /// legacy Python driver UUID format. #[cfg(all(feature = "serde_with-3", feature = "uuid-1"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "serde_with-3", feature = "uuid-1"))))]