Skip to content

Commit

Permalink
RUST-687 Introduce serde helpers for legacy UUID formats (crossterm-r…
Browse files Browse the repository at this point in the history
  • Loading branch information
kenkoooo authored May 20, 2021
1 parent 0af081b commit 87f8056
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 0 deletions.
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

0 comments on commit 87f8056

Please sign in to comment.