Skip to main content

uuid/
non_nil.rs

1//! A wrapper type for nil UUIDs that provides a more memory-efficient
2//! `Option<NonNilUuid>` representation.
3
4use std::{cmp::Ordering, fmt, num::NonZeroU128};
5
6use crate::{
7    error::{Error, ErrorKind},
8    Uuid,
9};
10
11/// A UUID that is guaranteed not to be the [nil UUID](https://www.ietf.org/rfc/rfc9562.html#name-nil-uuid).
12///
13/// This is useful for representing optional UUIDs more efficiently, as `Option<NonNilUuid>`
14/// takes up the same space as `Uuid`.
15///
16/// Note that `Uuid`s created by the following methods are guaranteed to be non-nil:
17///
18/// - [`Uuid::new_v1`]
19/// - [`Uuid::now_v1`]
20/// - [`Uuid::new_v3`]
21/// - [`Uuid::new_v4`]
22/// - [`Uuid::new_v5`]
23/// - [`Uuid::new_v6`]
24/// - [`Uuid::now_v6`]
25/// - [`Uuid::new_v7`]
26/// - [`Uuid::now_v7`]
27/// - [`Uuid::new_v8`]
28///
29/// # ABI
30///
31/// The `NonNilUuid` type does not yet have a stable ABI. Its representation or alignment
32/// may change. It is currently only guaranteed that `NonNilUuid` and `Option<NonNilUuid>`
33/// are the same size as `Uuid`.
34#[repr(transparent)]
35#[derive(#[automatically_derived]impl ::core::marker::Copy for NonNilUuid { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for NonNilUuid {
}
#[automatically_derived]impl ::core::clone::Clone for NonNilUuid {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<NonZeroU128>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    NonNilUuid {
}
#[automatically_derived]impl ::core::cmp::PartialEq for NonNilUuid {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for NonNilUuid {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<NonZeroU128>;
    }
}Eq, #[automatically_derived]impl ::core::hash::Hash for NonNilUuid {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash, #[automatically_derived]impl ::core::cmp::PartialOrd for NonNilUuid {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]impl ::core::cmp::Ord for NonNilUuid {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord)]
36pub struct NonNilUuid(NonZeroU128);
37
38impl fmt::Debug for NonNilUuid {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        fmt::Debug::fmt(&Uuid::from(*self), f)
41    }
42}
43
44impl fmt::Display for NonNilUuid {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        fmt::Display::fmt(&Uuid::from(*self), f)
47    }
48}
49
50impl PartialEq<Uuid> for NonNilUuid {
51    fn eq(&self, other: &Uuid) -> bool {
52        self.get() == *other
53    }
54}
55
56impl PartialEq<NonNilUuid> for Uuid {
57    fn eq(&self, other: &NonNilUuid) -> bool {
58        *self == other.get()
59    }
60}
61
62impl PartialOrd<Uuid> for NonNilUuid {
63    fn partial_cmp(&self, other: &Uuid) -> Option<Ordering> {
64        self.get().partial_cmp(other)
65    }
66}
67
68impl PartialOrd<NonNilUuid> for Uuid {
69    fn partial_cmp(&self, other: &NonNilUuid) -> Option<Ordering> {
70        self.partial_cmp(&other.get())
71    }
72}
73
74impl NonNilUuid {
75    /// Creates a non-nil UUID if the value is non-nil.
76    pub const fn new(uuid: Uuid) -> Option<Self> {
77        match NonZeroU128::new(uuid.as_u128()) {
78            Some(non_nil) => Some(NonNilUuid(non_nil)),
79            None => None,
80        }
81    }
82
83    /// Creates a non-nil without checking whether the value is non-nil. This results in undefined behavior if the value is nil.
84    ///
85    /// # Safety
86    ///
87    /// The value must not be nil.
88    pub const unsafe fn new_unchecked(uuid: Uuid) -> Self {
89        NonNilUuid(unsafe { NonZeroU128::new_unchecked(uuid.as_u128()) })
90    }
91
92    /// Get the underlying [`Uuid`] value.
93    #[inline]
94    pub const fn get(self) -> Uuid {
95        Uuid::from_u128(self.0.get())
96    }
97
98    /// Creates a non-nil version 1 UUID.
99    ///
100    /// See [`Uuid::new_v1`] for details.
101    #[cfg(feature = "v1")]
102    pub fn new_v1(ts: crate::Timestamp, node_id: &[u8; 6]) -> Self {
103        // SAFETY: `Uuid::new_v1` sets the version bits, so its result cannot be nil.
104        unsafe { Self::new_unchecked(Uuid::new_v1(ts, node_id)) }
105    }
106
107    /// Creates a non-nil version 1 UUID from the current time and node ID.
108    ///
109    /// See [`Uuid::now_v1`] for details.
110    #[cfg(all(feature = "v1", feature = "std", feature = "rng"))]
111    pub fn now_v1(node_id: &[u8; 6]) -> Self {
112        // SAFETY: `Uuid::now_v1` sets the version bits, so its result cannot be nil.
113        unsafe { Self::new_unchecked(Uuid::now_v1(node_id)) }
114    }
115
116    /// Creates a non-nil version 3 UUID from a namespace and name.
117    ///
118    /// See [`Uuid::new_v3`] for details.
119    #[cfg(feature = "v3")]
120    pub fn new_v3(namespace: &Uuid, name: &[u8]) -> Self {
121        // SAFETY: `Uuid::new_v3` sets the version bits, so its result cannot be nil.
122        unsafe { Self::new_unchecked(Uuid::new_v3(namespace, name)) }
123    }
124
125    /// Creates a non-nil random version 4 UUID.
126    ///
127    /// See [`Uuid::new_v4`] for details.
128    #[cfg(feature = "v4")]
129    pub fn new_v4() -> Self {
130        // SAFETY: `Uuid::new_v4` sets the version bits, so its result cannot be nil.
131        unsafe { Self::new_unchecked(Uuid::new_v4()) }
132    }
133
134    /// Creates a non-nil version 5 UUID from a namespace and name.
135    ///
136    /// See [`Uuid::new_v5`] for details.
137    #[cfg(feature = "v5")]
138    pub fn new_v5(namespace: &Uuid, name: &[u8]) -> Self {
139        // SAFETY: `Uuid::new_v5` sets the version bits, so its result cannot be nil.
140        unsafe { Self::new_unchecked(Uuid::new_v5(namespace, name)) }
141    }
142
143    /// Creates a non-nil version 6 UUID from a timestamp and node ID.
144    ///
145    /// See [`Uuid::new_v6`] for details.
146    #[cfg(feature = "v6")]
147    pub fn new_v6(ts: crate::Timestamp, node_id: &[u8; 6]) -> Self {
148        // SAFETY: `Uuid::new_v6` sets the version bits, so its result cannot be nil.
149        unsafe { Self::new_unchecked(Uuid::new_v6(ts, node_id)) }
150    }
151
152    /// Creates a non-nil version 6 UUID from the current time and node ID.
153    ///
154    /// See [`Uuid::now_v6`] for details.
155    #[cfg(all(feature = "v6", feature = "std", feature = "rng"))]
156    pub fn now_v6(node_id: &[u8; 6]) -> Self {
157        // SAFETY: `Uuid::now_v6` sets the version bits, so its result cannot be nil.
158        unsafe { Self::new_unchecked(Uuid::now_v6(node_id)) }
159    }
160
161    /// Creates a non-nil version 7 UUID from a timestamp.
162    ///
163    /// See [`Uuid::new_v7`] for details.
164    #[cfg(feature = "v7")]
165    pub fn new_v7(ts: crate::Timestamp) -> Self {
166        // SAFETY: `Uuid::new_v7` sets the version bits, so its result cannot be nil.
167        unsafe { Self::new_unchecked(Uuid::new_v7(ts)) }
168    }
169
170    /// Creates a non-nil version 7 UUID from the current time.
171    ///
172    /// See [`Uuid::now_v7`] for details.
173    #[cfg(all(feature = "v7", feature = "std"))]
174    pub fn now_v7() -> Self {
175        // SAFETY: `Uuid::now_v7` sets the version bits, so its result cannot be nil.
176        unsafe { Self::new_unchecked(Uuid::now_v7()) }
177    }
178
179    /// Creates a non-nil version 8 UUID from custom bytes.
180    ///
181    /// See [`Uuid::new_v8`] for details.
182    #[cfg(feature = "v8")]
183    pub const fn new_v8(bytes: [u8; 16]) -> Self {
184        // SAFETY: `Uuid::new_v8` sets the version bits, so its result cannot be nil.
185        unsafe { Self::new_unchecked(Uuid::new_v8(bytes)) }
186    }
187}
188
189impl From<NonNilUuid> for Uuid {
190    /// Converts a [`NonNilUuid`] back into a [`Uuid`].
191    ///
192    /// # Examples
193    /// ```
194    /// # use uuid::{NonNilUuid, Uuid};
195    /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
196    /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
197    /// let uuid_again = Uuid::from(non_nil);
198    ///
199    /// assert_eq!(uuid, uuid_again);
200    /// ```
201    fn from(non_nil: NonNilUuid) -> Self {
202        Uuid::from_u128(non_nil.0.get())
203    }
204}
205
206impl TryFrom<Uuid> for NonNilUuid {
207    type Error = Error;
208
209    /// Attempts to convert a [`Uuid`] into a [`NonNilUuid`].
210    ///
211    /// # Examples
212    /// ```
213    /// # use uuid::{NonNilUuid, Uuid};
214    /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
215    /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
216    /// ```
217    fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
218        NonZeroU128::new(uuid.as_u128())
219            .map(Self)
220            .ok_or(Error(ErrorKind::Nil))
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn test_non_nil_with_option_size() {
230        assert_eq!(
231            std::mem::size_of::<Option<NonNilUuid>>(),
232            std::mem::size_of::<Uuid>()
233        );
234    }
235
236    #[test]
237    fn test_non_nil() {
238        let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
239
240        assert_eq!(Uuid::from(NonNilUuid::try_from(uuid).unwrap()), uuid);
241        assert_eq!(NonNilUuid::new(uuid).unwrap(), uuid);
242        assert_eq!(unsafe { NonNilUuid::new_unchecked(uuid) }, uuid);
243
244        assert!(NonNilUuid::try_from(Uuid::nil()).is_err());
245        assert!(NonNilUuid::new(Uuid::nil()).is_none());
246    }
247
248    #[test]
249    fn test_non_nil_formatting() {
250        let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
251        let non_nil = NonNilUuid::try_from(uuid).unwrap();
252
253        assert_eq!(format!("{uuid}"), format!("{non_nil}"));
254        assert_eq!(format!("{uuid:?}"), format!("{non_nil:?}"));
255    }
256
257    #[test]
258    fn test_non_nil_ord() {
259        let uuid1 = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
260        let uuid2 = Uuid::from_u128(0x0123456789abcdef0123456789abcdf0);
261
262        let non_nil1 = NonNilUuid::try_from(uuid1).unwrap();
263        let non_nil2 = NonNilUuid::try_from(uuid2).unwrap();
264
265        // Ordering between NonNilUuid instances
266        assert!(non_nil1 < non_nil2);
267        assert!(non_nil2 > non_nil1);
268
269        // Ordering between NonNilUuid and Uuid
270        assert!(non_nil1 < uuid2);
271        assert!(non_nil2 > uuid1);
272
273        // Ordering between Uuid and NonNilUuid
274        assert!(uuid1 < non_nil2);
275        assert!(uuid2 > non_nil1);
276
277        // Equality
278        let non_nil1_copy = NonNilUuid::try_from(uuid1).unwrap();
279        assert_eq!(non_nil1, non_nil1_copy);
280        assert!(non_nil1 <= non_nil1_copy);
281        assert!(non_nil1 >= non_nil1_copy);
282
283        // Equality between NonNilUuid and Uuid
284        assert_eq!(non_nil1, uuid1);
285        assert_eq!(uuid1, non_nil1);
286        assert!(non_nil1 >= uuid1);
287        assert!(non_nil1 <= uuid1);
288        assert!(uuid1 >= non_nil1);
289        assert!(uuid1 <= non_nil1);
290    }
291}