Skip to content

RUST-687 Support deserializing binary of UuidOld #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 20, 2021
Merged
122 changes: 122 additions & 0 deletions src/serde_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: Serializer>(val: &u32, serializer: S) -> Result<S::Ok, S::Error> {
Expand Down Expand Up @@ -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<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
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<Uuid, D::Error>
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<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
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<Uuid, D::Error>
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<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
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<Uuid, D::Error>
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.
///
Expand Down
45 changes: 45 additions & 0 deletions src/tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down