bson/uuid.rs
1//! UUID support for BSON.
2//!
3//! ## The [`crate::Uuid`] type
4//!
5//! The BSON format supports UUIDs via the "binary" type with the UUID subtype (4).
6//! To facilitate working with these UUID-subtyped binary values, this crate provides a
7//! [`crate::Uuid`] type, whose `serde` implementation automatically serializes to and deserializes
8//! from binary values with subtype 4.
9//!
10//! The popular [`uuid`](https://docs.rs/uuid) crate also provides a
11//! [UUID type](https://docs.rs/uuid/latest/uuid/struct.Uuid.html),
12//! though its `serde` implementation does not produce or parse subtype 4
13//! binary values. Instead, when serialized with `bson::to_bson`, it produces as a string, and when
14//! serialized with `bson::to_vec`, it produces a binary value with subtype _0_ rather than 4.
15//! Because of this, it is highly recommended to use the [`crate::Uuid`] type when working with BSON
16//! instead of the `uuid` crate's [`Uuid`], since [`crate::Uuid`] correctly produces subtype 4
17//! binary values via either serialization function.
18//!
19//! e.g.
20//!
21//! ``` rust
22//! # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))]
23//! # {
24//! # use uuid as uuid;
25//! use bson::doc;
26//! use serde::{Serialize, Deserialize};
27//! use serde_with::serde_as;
28//!
29//!
30//! #[serde_as]
31//! #[derive(Serialize, Deserialize)]
32//! struct Foo {
33//! /// serializes as a String or subtype 0 BSON binary, depending
34//! /// on whether `bson::to_bson` or `bson::to_vec` is used.
35//! uuid: uuid::Uuid,
36//!
37//! /// serializes as a BSON binary with subtype 4 when either is used.
38//! bson_uuid: bson::Uuid,
39//!
40//! /// serializes as a BSON binary with subtype 4 when either is used.
41//! /// this requires the "uuid-1" and "serde_with-3" feature flags
42//! #[serde_as(as = "bson::serde_helpers::uuid_1::AsBinary")]
43//! uuid_as_bson: uuid::Uuid,
44//! }
45//! # };
46//! ```
47//!
48//! ## The `uuid-1` feature flag
49//!
50//! To facilitate the conversion between [`crate::Uuid`] values and the `uuid` crate's [`Uuid`]
51//! values, the `uuid-1` feature flag can be enabled. This flag exposes a number of convenient
52//! conversions, including the `crate::Uuid::to_uuid_1` method and the `From<uuid::Uuid>`
53//! implementation for [`Bson`], which allows the `uuid` crate's [`Uuid`] values to be used in the
54//! `doc!` and `bson!` macros.
55//!
56//! ```
57//! # #[cfg(feature = "uuid-1")]
58//! # {
59//! # use uuid as uuid;
60//! use bson::doc;
61//!
62//! // this automatic conversion does not require any feature flags
63//! let query = doc! {
64//! "uuid": bson::Uuid::new(),
65//! };
66//!
67//! // but this automatic conversion requires the "uuid-1" feature flag
68//! let query = doc! {
69//! "uuid": uuid::Uuid::new_v4(),
70//! };
71//!
72//! // this also requires the "uuid-1" feature flag.
73//! let uuid = bson::Uuid::new().to_uuid_1();
74//! # };
75//! ```
76//!
77//! ## Serde conversion
78//!
79//! Fields using the [`uuid::Uuid`] type can be (de)serialized as BSON using the converters provided
80//! in [`serde_helpers`](crate::serde_helpers):
81//!
82//! ```
83//! # #[cfg(feature = "uuid-1")]
84//! # {
85//! use uuid;
86//! use serde::{Deserialize, Serialize};
87//! use bson::{doc, serde_helpers::uuid_1};
88//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
89//! struct Foo {
90//! /// Serializes as a BSON binary rather than using [`uuid::Uuid`]'s serialization
91//! #[serde(with = "uuid_1::AsBinary")]
92//! as_bson: uuid::Uuid,
93//! }
94//!
95//! let foo = Foo {
96//! as_bson: uuid::Uuid::new_v4(),
97//! };
98//!
99//! let expected = doc! {
100//! "as_bson": bson::Uuid::from(foo.as_bson),
101//! };
102//!
103//! assert_eq!(bson::serialize_to_document(&foo)?, expected);
104//! # }
105//! # Ok::<(), Box<dyn std::error::Error>>(())
106//! ```
107//!
108//! ## The `serde_with-3` feature flag
109//!
110//! The `serde_with-3` feature can be enabled to support more ergonomic serde attributes for
111//! conversions. The main benefit of this compared to the regular `serde_helpers` is that
112//! `serde_with-3` can handle nested [`uuid::Uuid`] values (e.g. in [`Option`]), whereas the former
113//! only works on fields that are exactly [`uuid::Uuid`].
114//! ```
115//! # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))]
116//! # {
117//! # use uuid;
118//! use serde::{Deserialize, Serialize};
119//! use bson::{doc, serde_helpers::uuid_1};
120//!
121//! #[serde_with::serde_as]
122//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
123//! struct Foo {
124//! /// Serializes as a BSON binary rather than using [`uuid::Uuid`]'s serialization
125//! #[serde_as(as = "Option<uuid_1::AsBinary>")]
126//! as_bson: Option<uuid::Uuid>,
127//! }
128//!
129//! let foo = Foo {
130//! as_bson: Some(uuid::Uuid::new_v4()),
131//! };
132//!
133//! let expected = doc! {
134//! "as_bson": bson::Uuid::from(foo.as_bson.unwrap()),
135//! };
136//!
137//! assert_eq!(bson::serialize_to_document(&foo)?, expected);
138//! # }
139//! # Ok::<(), Box<dyn std::error::Error>>(())
140//! ```
141//!
142//! ## Using [`crate::Uuid`] with non-BSON formats
143//!
144//! [`crate::Uuid`]'s `serde` implementation is the same as [`uuid::Uuid`]'s
145//! for non-BSON formats such as JSON:
146//! ``` rust
147//! # #[cfg(feature = "uuid-1")]
148//! # {
149//! # use uuid as uuid;
150//! # use serde::{Serialize, Deserialize};
151//! # #[derive(Serialize, Deserialize)]
152//! # struct Foo {
153//! # uuid: uuid::Uuid,
154//! # bson_uuid: bson::Uuid,
155//! # }
156//! use serde_json::json;
157//!
158//! let uuid = uuid::Uuid::new_v4();
159//! let bson_uuid: bson::Uuid = uuid.into();
160//! let foo = Foo { uuid, bson_uuid, };
161//!
162//! let json = serde_json::to_value(&foo)?;
163//! assert_eq!(json, json!({ "uuid": uuid.to_string(), "bson_uuid": uuid.to_string() }));
164//! # }
165//! # Ok::<(), Box::<dyn std::error::Error>>(())
166//! ```
167#[cfg(test)]
168mod test;
169
170use std::{
171 fmt::{self, Display},
172 str::FromStr,
173};
174
175use crate::{
176 error::{Error, Result},
177 spec::BinarySubtype,
178 Binary,
179 Bson,
180};
181
182/// Special type name used in the [`Uuid`] serialization implementation to indicate a BSON
183/// UUID is being serialized or deserialized. The BSON serializers/deserializers will handle this
184/// name specially, but other serializers/deserializers will just ignore it and use [`uuid::Uuid`]'s
185/// serde integration.
186#[cfg(feature = "serde")]
187pub(crate) const UUID_NEWTYPE_NAME: &str = "$__bson_private_uuid";
188
189/// A struct modeling a BSON UUID value (i.e. a Binary value with subtype 4).
190///
191/// This type should be used instead of [`uuid::Uuid`](https://docs.rs/uuid/latest/uuid/struct.Uuid.html)
192/// when serializing to or deserializing from BSON, since
193/// [`uuid::Uuid`](https://docs.rs/uuid/latest/uuid/struct.Uuid.html)'s `serde` implementation doesn't
194/// produce or parse BSON UUIDs.
195///
196/// To enable interop with the [`Uuid`] type from the `uuid` crate, enable the `uuid-0_8` feature
197/// flag.
198///
199/// For more information on the usage of this type, see the [`uuid`] module-level documentation.
200///
201/// Note: due to an issue in serde (see [here](https://github.com/serde-rs/serde/issues/2106)), this type
202/// will also allow deserialization from 16 byte + subtype 0 Binary values in BSON if part of a
203/// `#[serde(flatten)]` chain. This behavior shouldn't be relied upon as it may be fixed at some
204/// point in the future.
205#[derive(Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
206pub struct Uuid {
207 uuid: uuid::Uuid,
208}
209
210impl Uuid {
211 /// Creates a random UUID.
212 ///
213 /// This uses the operating system's RNG as the source of random numbers. If you'd like to use a
214 /// custom generator, generate random bytes and pass them to [`Uuid::from_bytes`] instead.
215 pub fn new() -> Self {
216 Self {
217 uuid: uuid::Uuid::new_v4(),
218 }
219 }
220
221 /// Creates a [`Uuid`] using the supplied big-endian bytes.
222 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
223 Self::from_external_uuid(uuid::Uuid::from_bytes(bytes))
224 }
225
226 /// Creates a [`Uuid`] from the provided hex string.
227 pub fn parse_str(input: impl AsRef<str>) -> Result<Self> {
228 let uuid = uuid::Uuid::parse_str(input.as_ref()).map_err(Error::invalid_uuid_string)?;
229 Ok(Self::from_external_uuid(uuid))
230 }
231
232 pub(crate) const fn from_external_uuid(uuid: uuid::Uuid) -> Self {
233 Self { uuid }
234 }
235
236 /// Returns an array of 16 bytes containing the [`Uuid`]'s data.
237 pub const fn bytes(self) -> [u8; 16] {
238 *self.uuid.as_bytes()
239 }
240}
241
242impl Default for Uuid {
243 fn default() -> Self {
244 Self::new()
245 }
246}
247
248impl FromStr for Uuid {
249 type Err = Error;
250
251 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
252 Self::parse_str(s)
253 }
254}
255
256#[cfg(feature = "uuid-1")]
257impl Uuid {
258 /// Create a [`Uuid`] from a [`uuid::Uuid`](https://docs.rs/uuid/0.8/uuid/struct.Uuid.html) from
259 /// the [`uuid`](https://docs.rs/uuid/0.8) crate.
260 pub fn from_uuid_1(uuid: uuid::Uuid) -> Self {
261 Self::from_external_uuid(uuid)
262 }
263
264 /// Convert this [`Uuid`] to a [`uuid::Uuid`](https://docs.rs/uuid/0.8/uuid/struct.Uuid.html) from
265 /// the [`uuid`](https://docs.rs/uuid/0.8) crate.
266 pub fn to_uuid_1(self) -> uuid::Uuid {
267 self.uuid
268 }
269}
270
271#[cfg(feature = "serde")]
272impl serde::Serialize for Uuid {
273 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
274 where
275 S: serde::Serializer,
276 {
277 serializer.serialize_newtype_struct(UUID_NEWTYPE_NAME, &self.uuid)
278 }
279}
280
281#[cfg(feature = "serde")]
282impl<'de> serde::Deserialize<'de> for Uuid {
283 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
284 where
285 D: serde::Deserializer<'de>,
286 {
287 match deserializer.deserialize_newtype_struct(UUID_NEWTYPE_NAME, crate::de::BsonVisitor)? {
288 // Need to support deserializing from generic subtypes for non-BSON formats.
289 // When using the BSON deserializer, the newtype name will ensure the subtype is only
290 // ever BinarySubtype::Uuid.
291 Bson::Binary(b)
292 if matches!(b.subtype, BinarySubtype::Uuid | BinarySubtype::Generic) =>
293 {
294 let uuid =
295 uuid::Uuid::from_slice(b.bytes.as_slice()).map_err(serde::de::Error::custom)?;
296 Ok(Self::from_external_uuid(uuid))
297 }
298 Bson::Binary(b) if b.subtype == BinarySubtype::UuidOld => {
299 Err(serde::de::Error::custom(
300 "received legacy UUID (subtype 3) but expected regular UUID (subtype 4)",
301 ))
302 }
303 Bson::String(s) => {
304 use std::str::FromStr as _;
305 let uuid = uuid::Uuid::from_str(s.as_str()).map_err(serde::de::Error::custom)?;
306 Ok(Self::from_external_uuid(uuid))
307 }
308 b => Err(serde::de::Error::invalid_type(b.as_unexpected(), &"a UUID")),
309 }
310 }
311}
312
313impl Display for Uuid {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 self.uuid.fmt(f)
316 }
317}
318
319impl std::fmt::Debug for Uuid {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 std::fmt::Debug::fmt(&self.uuid, f)
322 }
323}
324
325impl From<Uuid> for Binary {
326 fn from(uuid: Uuid) -> Self {
327 Binary {
328 subtype: BinarySubtype::Uuid,
329 bytes: uuid.bytes().to_vec(),
330 }
331 }
332}
333
334impl From<Uuid> for Bson {
335 fn from(u: Uuid) -> Self {
336 Bson::Binary(u.into())
337 }
338}
339
340#[cfg(feature = "uuid-1")]
341impl From<uuid::Uuid> for Uuid {
342 fn from(u: uuid::Uuid) -> Self {
343 Self::from_uuid_1(u)
344 }
345}
346
347#[cfg(feature = "uuid-1")]
348impl From<Uuid> for uuid::Uuid {
349 fn from(s: Uuid) -> Self {
350 s.to_uuid_1()
351 }
352}
353
354/// Enum of the possible representations to use when converting between [`Uuid`] and [`Binary`].
355/// This enum is necessary because the different drivers used to have different ways of encoding
356/// UUIDs, with the BSON subtype: 0x03 (UUID old).
357/// If a UUID has been serialized with a particular representation, it MUST
358/// be deserialized with the same representation.
359///
360/// Example:
361/// ```
362/// use bson::{Binary, uuid::{Uuid, UuidRepresentation}};
363///
364/// let uuid = Uuid::parse_str("00112233445566778899AABBCCDDEEFF")?;
365/// let bin = Binary::from_uuid_with_representation(uuid, UuidRepresentation::PythonLegacy);
366///
367/// // This conversion fails, since the binary holds a PythonLegacy UUID, so we're required to specify
368/// // that.
369/// assert!(bin.to_uuid().is_err());
370///
371/// // This conversion succeeds, since we specified the correct representation.
372/// let new_uuid = bin.to_uuid_with_representation(UuidRepresentation::PythonLegacy)?;
373/// assert_eq!(new_uuid, uuid);
374///
375/// # Ok::<(), Box::<dyn std::error::Error>>(())
376/// ```
377#[non_exhaustive]
378#[derive(PartialEq, Clone, Copy, Debug)]
379pub enum UuidRepresentation {
380 /// The canonical representation of UUIDs in BSON (binary with subtype 0x04)
381 Standard,
382 /// The legacy representation of UUIDs in BSON used by the C# driver (binary subtype 0x03)
383 CSharpLegacy,
384 /// The legacy representation of UUIDs in BSON used by the Java driver (binary subtype 0x03)
385 JavaLegacy,
386 /// The legacy representation of UUIDs in BSON used by the Python driver, which is the same
387 /// format as STANDARD, but has binary subtype 0x03
388 PythonLegacy,
389}
390
391impl Binary {
392 /// Serializes a [`Uuid`] into BSON [`Binary`] type
393 pub fn from_uuid(uuid: Uuid) -> Self {
394 Binary::from(uuid)
395 }
396
397 /// Serializes a [`Uuid`] into BSON binary type and takes the desired representation as a
398 /// parameter. `Binary::from_uuid_with_representation(uuid, UuidRepresentation::Standard)` is
399 /// equivalent to `Binary::from_uuid(uuid)`.
400 ///
401 /// See the documentation for [`UuidRepresentation`] for more information on the possible
402 /// representations.
403 pub fn from_uuid_with_representation(uuid: Uuid, rep: UuidRepresentation) -> Self {
404 match rep {
405 UuidRepresentation::Standard => Binary::from_uuid(uuid),
406 UuidRepresentation::CSharpLegacy => {
407 let mut bytes = uuid.bytes().to_vec();
408 bytes[0..4].reverse();
409 bytes[4..6].reverse();
410 bytes[6..8].reverse();
411 Binary {
412 subtype: BinarySubtype::UuidOld,
413 bytes,
414 }
415 }
416 UuidRepresentation::PythonLegacy => Binary {
417 subtype: BinarySubtype::UuidOld,
418 bytes: uuid.bytes().to_vec(),
419 },
420 UuidRepresentation::JavaLegacy => {
421 let mut bytes = uuid.bytes().to_vec();
422 bytes[0..8].reverse();
423 bytes[8..16].reverse();
424 Binary {
425 subtype: BinarySubtype::UuidOld,
426 bytes,
427 }
428 }
429 }
430 }
431
432 /// Deserializes a BSON [`Binary`] type into a [`Uuid`] according to the provided
433 /// representation. If the representation does not match the [`Binary`], an error will be
434 /// returned.
435 ///
436 /// See the documentation for [`UuidRepresentation`] for more information on the possible
437 /// representations.
438 pub fn to_uuid_with_representation(&self, rep: UuidRepresentation) -> Result<Uuid> {
439 // If representation is non-standard, then its subtype must be UuidOld
440 if rep != UuidRepresentation::Standard && self.subtype != BinarySubtype::UuidOld {
441 return Err(Error::uuid_representation_mismatch(
442 rep,
443 self.subtype,
444 BinarySubtype::UuidOld,
445 ));
446 }
447 // If representation is standard, then its subtype must be Uuid
448 if rep == UuidRepresentation::Standard && self.subtype != BinarySubtype::Uuid {
449 return Err(Error::uuid_representation_mismatch(
450 rep,
451 self.subtype,
452 BinarySubtype::UuidOld,
453 ));
454 }
455 // Must be 16 bytes long
456 if self.bytes.len() != 16 {
457 return Err(Error::invalid_uuid_length(self.bytes.len()));
458 }
459 let mut buf = [0u8; 16];
460 buf.copy_from_slice(&self.bytes);
461 Ok(match rep {
462 UuidRepresentation::Standard => Uuid::from_bytes(buf),
463 UuidRepresentation::CSharpLegacy => {
464 buf[0..4].reverse();
465 buf[4..6].reverse();
466 buf[6..8].reverse();
467 Uuid::from_bytes(buf)
468 }
469 UuidRepresentation::PythonLegacy => Uuid::from_bytes(buf),
470 UuidRepresentation::JavaLegacy => {
471 buf[0..8].reverse();
472 buf[8..16].reverse();
473 Uuid::from_bytes(buf)
474 }
475 })
476 }
477
478 /// Deserializes a BSON [`Binary`] type into a [`Uuid`] using the standard
479 /// representation.
480 pub fn to_uuid(&self) -> Result<Uuid> {
481 self.to_uuid_with_representation(UuidRepresentation::Standard)
482 }
483}
484
485#[cfg(feature = "uuid-1")]
486impl From<uuid::Uuid> for Binary {
487 fn from(uuid: uuid::Uuid) -> Self {
488 Binary {
489 subtype: BinarySubtype::Uuid,
490 bytes: uuid.as_bytes().to_vec(),
491 }
492 }
493}