diff --git a/src/serde_helpers.rs b/src/serde_helpers.rs index 6a7710410..036c6e26e 100644 --- a/src/serde_helpers.rs +++ b/src/serde_helpers.rs @@ -34,6 +34,18 @@ pub use uuid_0_8_as_binary::{ deserialize as deserialize_uuid_0_8_from_binary, serialize as serialize_uuid_0_8_as_binary, }; +pub use uuid_0_8_as_c_sharp_legacy_binary::{ + deserialize as deserialize_uuid_from_c_sharp_legacy_binary, + serialize as serialize_uuid_as_c_sharp_legacy_binary, +}; +pub use uuid_0_8_as_java_legacy_binary::{ + deserialize as deserialize_uuid_from_java_legacy_binary, + serialize as serialize_uuid_as_java_legacy_binary, +}; +pub use uuid_0_8_as_python_legacy_binary::{ + deserialize as deserialize_uuid_from_python_legacy_binary, + serialize as serialize_uuid_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 { @@ -341,6 +353,116 @@ pub mod uuid_0_8_as_binary { } } +pub mod uuid_0_8_as_java_legacy_binary { + use crate::{spec::BinarySubtype, Binary}; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use std::result::Result; + use uuid::Uuid; + + /// Serializes a Uuid as a Binary in a Java Legacy UUID format. + pub fn serialize(val: &Uuid, serializer: S) -> Result { + let mut bytes = val.as_bytes().to_vec(); + bytes[0..8].reverse(); + bytes[8..16].reverse(); + let binary = Binary { + subtype: BinarySubtype::UuidOld, + bytes, + }; + binary.serialize(serializer) + } + + /// Deserializes a Uuid from a Binary in a Java Legacy UUID format. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let binary = Binary::deserialize(deserializer)?; + if binary.subtype != BinarySubtype::UuidOld { + Err(de::Error::custom("expecting BinarySubtype::UuidOld")) + } else if binary.bytes.len() != 16 { + Err(de::Error::custom("expecting 16 bytes")) + } else { + let mut buf = [0u8; 16]; + buf.copy_from_slice(&binary.bytes); + buf[0..8].reverse(); + buf[8..16].reverse(); + Ok(Uuid::from_bytes(buf)) + } + } +} + +pub mod uuid_0_8_as_python_legacy_binary { + use crate::{spec::BinarySubtype, Binary}; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use std::result::Result; + use uuid::Uuid; + + /// Serializes a Uuid as a Binary in a Python Legacy UUID format. + pub fn serialize(val: &Uuid, serializer: S) -> Result { + let binary = Binary { + subtype: BinarySubtype::UuidOld, + bytes: val.as_bytes().to_vec(), + }; + binary.serialize(serializer) + } + + /// Deserializes a Uuid from a Binary in a Python Legacy UUID format. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let binary = Binary::deserialize(deserializer)?; + if binary.subtype != BinarySubtype::UuidOld { + Err(de::Error::custom("expecting BinarySubtype::UuidOld")) + } else if binary.bytes.len() != 16 { + Err(de::Error::custom("expecting 16 bytes")) + } else { + let mut buf = [0u8; 16]; + buf.copy_from_slice(&binary.bytes); + Ok(Uuid::from_bytes(buf)) + } + } +} +pub mod uuid_0_8_as_c_sharp_legacy_binary { + use crate::{spec::BinarySubtype, Binary}; + use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use std::result::Result; + use uuid::Uuid; + + /// Serializes a Uuid as a Binary in a C# Legacy UUID format. + pub fn serialize(val: &Uuid, serializer: S) -> Result { + let mut bytes = val.as_bytes().to_vec(); + bytes[0..4].reverse(); + bytes[4..6].reverse(); + bytes[6..8].reverse(); + let binary = Binary { + subtype: BinarySubtype::UuidOld, + bytes, + }; + binary.serialize(serializer) + } + + /// Deserializes a Uuid from a Binary in a C# Legacy UUID format. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let binary = Binary::deserialize(deserializer)?; + if binary.subtype != BinarySubtype::UuidOld { + Err(de::Error::custom("expecting BinarySubtype::UuidOld")) + } else if binary.bytes.len() != 16 { + Err(de::Error::custom("expecting 16 bytes")) + } else { + let mut buf = [0u8; 16]; + buf.copy_from_slice(&binary.bytes); + buf[0..4].reverse(); + buf[4..6].reverse(); + buf[6..8].reverse(); + Ok(Uuid::from_bytes(buf)) + } + } +} + /// 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 cc7b8ff76..d417082c8 100644 --- a/src/tests/serde.rs +++ b/src/tests/serde.rs @@ -559,6 +559,51 @@ fn test_de_db_pointer() { assert_eq!(foo.db_pointer, db_pointer.clone()); } +#[test] +fn test_serde_legacy_uuid() { + let _guard = LOCK.run_concurrently(); + + #[derive(Serialize, Deserialize)] + struct Foo { + #[serde(with = "serde_helpers::uuid_0_8_as_java_legacy_binary")] + java_legacy: Uuid, + #[serde(with = "serde_helpers::uuid_0_8_as_python_legacy_binary")] + python_legacy: Uuid, + #[serde(with = "serde_helpers::uuid_0_8_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 = 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 = 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_oid_string() { let _guard = LOCK.run_concurrently();