• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! A wrapper type for nil UUIDs that provides a more memory-efficient
2 //! `Option<NonNilUuid>` representation.
3 
4 use core::convert::TryFrom;
5 use std::{fmt, num::NonZeroU128};
6 
7 use crate::{
8     error::{Error, ErrorKind},
9     Uuid,
10 };
11 
12 /// A UUID that is guaranteed not to be the [nil UUID](https://www.ietf.org/rfc/rfc9562.html#name-nil-uuid).
13 ///
14 /// This is useful for representing optional UUIDs more efficiently, as `Option<NonNilUuid>`
15 /// takes up the same space as `Uuid`.
16 ///
17 /// Note that `Uuid`s created by the following methods are guaranteed to be non-nil:
18 ///
19 /// - [`Uuid::new_v1`]
20 /// - [`Uuid::now_v1`]
21 /// - [`Uuid::new_v3`]
22 /// - [`Uuid::new_v4`]
23 /// - [`Uuid::new_v5`]
24 /// - [`Uuid::new_v6`]
25 /// - [`Uuid::now_v6`]
26 /// - [`Uuid::new_v7`]
27 /// - [`Uuid::now_v7`]
28 /// - [`Uuid::new_v8`]
29 ///
30 /// # ABI
31 ///
32 /// The `NonNilUuid` type does not yet have a stable ABI. Its representation or alignment
33 /// may change. It is currently only guaranteed that `NonNilUuid` and `Option<NonNilUuid>`
34 /// are the same size as `Uuid`.
35 #[repr(transparent)]
36 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
37 pub struct NonNilUuid(NonZeroU128);
38 
39 impl fmt::Display for NonNilUuid {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result40     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41         write!(f, "{}", Uuid::from(*self))
42     }
43 }
44 
45 impl PartialEq<Uuid> for NonNilUuid {
eq(&self, other: &Uuid) -> bool46     fn eq(&self, other: &Uuid) -> bool {
47         self.get() == *other
48     }
49 }
50 
51 impl PartialEq<NonNilUuid> for Uuid {
eq(&self, other: &NonNilUuid) -> bool52     fn eq(&self, other: &NonNilUuid) -> bool {
53         *self == other.get()
54     }
55 }
56 
57 impl NonNilUuid {
58     /// Creates a non-nil UUID if the value is non-nil.
new(uuid: Uuid) -> Option<Self>59     pub const fn new(uuid: Uuid) -> Option<Self> {
60         match NonZeroU128::new(uuid.as_u128()) {
61             Some(non_nil) => Some(NonNilUuid(non_nil)),
62             None => None,
63         }
64     }
65 
66     /// Creates a non-nil without checking whether the value is non-nil. This results in undefined behavior if the value is nil.
67     ///
68     /// # Safety
69     ///
70     /// The value must not be nil.
new_unchecked(uuid: Uuid) -> Self71     pub const unsafe fn new_unchecked(uuid: Uuid) -> Self {
72         NonNilUuid(unsafe { NonZeroU128::new_unchecked(uuid.as_u128()) })
73     }
74 
75     /// Get the underlying [`Uuid`] value.
76     #[inline]
get(self) -> Uuid77     pub const fn get(self) -> Uuid {
78         Uuid::from_u128(self.0.get())
79     }
80 }
81 
82 impl From<NonNilUuid> for Uuid {
83     /// Converts a [`NonNilUuid`] back into a [`Uuid`].
84     ///
85     /// # Examples
86     /// ```
87     /// # use std::convert::TryFrom;
88     /// # use uuid::{NonNilUuid, Uuid};
89     /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
90     /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
91     /// let uuid_again = Uuid::from(non_nil);
92     ///
93     /// assert_eq!(uuid, uuid_again);
94     /// ```
from(non_nil: NonNilUuid) -> Self95     fn from(non_nil: NonNilUuid) -> Self {
96         Uuid::from_u128(non_nil.0.get())
97     }
98 }
99 
100 impl TryFrom<Uuid> for NonNilUuid {
101     type Error = Error;
102 
103     /// Attempts to convert a [`Uuid`] into a [`NonNilUuid`].
104     ///
105     /// # Examples
106     /// ```
107     /// # use std::convert::TryFrom;
108     /// # use uuid::{NonNilUuid, Uuid};
109     /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
110     /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
111     /// ```
try_from(uuid: Uuid) -> Result<Self, Self::Error>112     fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
113         NonZeroU128::new(uuid.as_u128())
114             .map(Self)
115             .ok_or(Error(ErrorKind::Nil))
116     }
117 }
118 
119 #[cfg(test)]
120 mod tests {
121     use super::*;
122 
123     #[test]
test_non_nil_with_option_size()124     fn test_non_nil_with_option_size() {
125         assert_eq!(
126             std::mem::size_of::<Option<NonNilUuid>>(),
127             std::mem::size_of::<Uuid>()
128         );
129     }
130 
131     #[test]
test_non_nil()132     fn test_non_nil() {
133         let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
134 
135         assert_eq!(Uuid::from(NonNilUuid::try_from(uuid).unwrap()), uuid);
136         assert_eq!(NonNilUuid::new(uuid).unwrap(), uuid);
137         assert_eq!(unsafe { NonNilUuid::new_unchecked(uuid) }, uuid);
138 
139         assert!(NonNilUuid::try_from(Uuid::nil()).is_err());
140         assert!(NonNilUuid::new(Uuid::nil()).is_none());
141     }
142 }
143