Skip to content

Commit 87f8056

Browse files
authored
RUST-687 Introduce serde helpers for legacy UUID formats (#239)
1 parent 0af081b commit 87f8056

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

src/serde_helpers.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ pub use uuid_0_8_as_binary::{
3434
deserialize as deserialize_uuid_0_8_from_binary,
3535
serialize as serialize_uuid_0_8_as_binary,
3636
};
37+
pub use uuid_0_8_as_c_sharp_legacy_binary::{
38+
deserialize as deserialize_uuid_from_c_sharp_legacy_binary,
39+
serialize as serialize_uuid_as_c_sharp_legacy_binary,
40+
};
41+
pub use uuid_0_8_as_java_legacy_binary::{
42+
deserialize as deserialize_uuid_from_java_legacy_binary,
43+
serialize as serialize_uuid_as_java_legacy_binary,
44+
};
45+
pub use uuid_0_8_as_python_legacy_binary::{
46+
deserialize as deserialize_uuid_from_python_legacy_binary,
47+
serialize as serialize_uuid_as_python_legacy_binary,
48+
};
3749

3850
/// Attempts to serialize a u32 as an i32. Errors if an exact conversion is not possible.
3951
pub fn serialize_u32_as_i32<S: Serializer>(val: &u32, serializer: S) -> Result<S::Ok, S::Error> {
@@ -341,6 +353,116 @@ pub mod uuid_0_8_as_binary {
341353
}
342354
}
343355

356+
pub mod uuid_0_8_as_java_legacy_binary {
357+
use crate::{spec::BinarySubtype, Binary};
358+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
359+
use std::result::Result;
360+
use uuid::Uuid;
361+
362+
/// Serializes a Uuid as a Binary in a Java Legacy UUID format.
363+
pub fn serialize<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
364+
let mut bytes = val.as_bytes().to_vec();
365+
bytes[0..8].reverse();
366+
bytes[8..16].reverse();
367+
let binary = Binary {
368+
subtype: BinarySubtype::UuidOld,
369+
bytes,
370+
};
371+
binary.serialize(serializer)
372+
}
373+
374+
/// Deserializes a Uuid from a Binary in a Java Legacy UUID format.
375+
pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
376+
where
377+
D: Deserializer<'de>,
378+
{
379+
let binary = Binary::deserialize(deserializer)?;
380+
if binary.subtype != BinarySubtype::UuidOld {
381+
Err(de::Error::custom("expecting BinarySubtype::UuidOld"))
382+
} else if binary.bytes.len() != 16 {
383+
Err(de::Error::custom("expecting 16 bytes"))
384+
} else {
385+
let mut buf = [0u8; 16];
386+
buf.copy_from_slice(&binary.bytes);
387+
buf[0..8].reverse();
388+
buf[8..16].reverse();
389+
Ok(Uuid::from_bytes(buf))
390+
}
391+
}
392+
}
393+
394+
pub mod uuid_0_8_as_python_legacy_binary {
395+
use crate::{spec::BinarySubtype, Binary};
396+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
397+
use std::result::Result;
398+
use uuid::Uuid;
399+
400+
/// Serializes a Uuid as a Binary in a Python Legacy UUID format.
401+
pub fn serialize<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
402+
let binary = Binary {
403+
subtype: BinarySubtype::UuidOld,
404+
bytes: val.as_bytes().to_vec(),
405+
};
406+
binary.serialize(serializer)
407+
}
408+
409+
/// Deserializes a Uuid from a Binary in a Python Legacy UUID format.
410+
pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
411+
where
412+
D: Deserializer<'de>,
413+
{
414+
let binary = Binary::deserialize(deserializer)?;
415+
if binary.subtype != BinarySubtype::UuidOld {
416+
Err(de::Error::custom("expecting BinarySubtype::UuidOld"))
417+
} else if binary.bytes.len() != 16 {
418+
Err(de::Error::custom("expecting 16 bytes"))
419+
} else {
420+
let mut buf = [0u8; 16];
421+
buf.copy_from_slice(&binary.bytes);
422+
Ok(Uuid::from_bytes(buf))
423+
}
424+
}
425+
}
426+
pub mod uuid_0_8_as_c_sharp_legacy_binary {
427+
use crate::{spec::BinarySubtype, Binary};
428+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
429+
use std::result::Result;
430+
use uuid::Uuid;
431+
432+
/// Serializes a Uuid as a Binary in a C# Legacy UUID format.
433+
pub fn serialize<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
434+
let mut bytes = val.as_bytes().to_vec();
435+
bytes[0..4].reverse();
436+
bytes[4..6].reverse();
437+
bytes[6..8].reverse();
438+
let binary = Binary {
439+
subtype: BinarySubtype::UuidOld,
440+
bytes,
441+
};
442+
binary.serialize(serializer)
443+
}
444+
445+
/// Deserializes a Uuid from a Binary in a C# Legacy UUID format.
446+
pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
447+
where
448+
D: Deserializer<'de>,
449+
{
450+
let binary = Binary::deserialize(deserializer)?;
451+
if binary.subtype != BinarySubtype::UuidOld {
452+
Err(de::Error::custom("expecting BinarySubtype::UuidOld"))
453+
} else if binary.bytes.len() != 16 {
454+
Err(de::Error::custom("expecting 16 bytes"))
455+
} else {
456+
let mut buf = [0u8; 16];
457+
buf.copy_from_slice(&binary.bytes);
458+
buf[0..4].reverse();
459+
buf[4..6].reverse();
460+
buf[6..8].reverse();
461+
Ok(Uuid::from_bytes(buf))
462+
}
463+
}
464+
}
465+
344466
/// Contains functions to serialize a u32 as a bson::Timestamp and deserialize a u32 from a
345467
/// bson::Timestamp. The u32 should represent seconds since the Unix epoch.
346468
///

src/tests/serde.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,51 @@ fn test_de_db_pointer() {
559559
assert_eq!(foo.db_pointer, db_pointer.clone());
560560
}
561561

562+
#[test]
563+
fn test_serde_legacy_uuid() {
564+
let _guard = LOCK.run_concurrently();
565+
566+
#[derive(Serialize, Deserialize)]
567+
struct Foo {
568+
#[serde(with = "serde_helpers::uuid_0_8_as_java_legacy_binary")]
569+
java_legacy: Uuid,
570+
#[serde(with = "serde_helpers::uuid_0_8_as_python_legacy_binary")]
571+
python_legacy: Uuid,
572+
#[serde(with = "serde_helpers::uuid_0_8_as_c_sharp_legacy_binary")]
573+
csharp_legacy: Uuid,
574+
}
575+
let uuid = Uuid::parse_str("00112233445566778899AABBCCDDEEFF").unwrap();
576+
let foo = Foo {
577+
java_legacy: uuid,
578+
python_legacy: uuid,
579+
csharp_legacy: uuid,
580+
};
581+
582+
let x = to_bson(&foo).unwrap();
583+
assert_eq!(
584+
x.as_document().unwrap(),
585+
&doc! {
586+
"java_legacy": Bson::Binary(Binary{
587+
subtype:BinarySubtype::UuidOld,
588+
bytes: hex::decode("7766554433221100FFEEDDCCBBAA9988").unwrap(),
589+
}),
590+
"python_legacy": Bson::Binary(Binary{
591+
subtype:BinarySubtype::UuidOld,
592+
bytes: hex::decode("00112233445566778899AABBCCDDEEFF").unwrap(),
593+
}),
594+
"csharp_legacy": Bson::Binary(Binary{
595+
subtype:BinarySubtype::UuidOld,
596+
bytes: hex::decode("33221100554477668899AABBCCDDEEFF").unwrap(),
597+
})
598+
}
599+
);
600+
601+
let foo: Foo = from_bson(x).unwrap();
602+
assert_eq!(foo.java_legacy, uuid);
603+
assert_eq!(foo.python_legacy, uuid);
604+
assert_eq!(foo.csharp_legacy, uuid);
605+
}
606+
562607
#[test]
563608
fn test_de_oid_string() {
564609
let _guard = LOCK.run_concurrently();

0 commit comments

Comments
 (0)