Coverage Report

Created: 2026-05-30 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.23.2/src/non_nil.rs
Line
Count
Source
1
//! A wrapper type for nil UUIDs that provides a more memory-efficient
2
//! `Option<NonNilUuid>` representation.
3
4
use std::{cmp::Ordering, fmt, num::NonZeroU128};
5
6
use 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(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
36
pub struct NonNilUuid(NonZeroU128);
37
38
impl fmt::Debug for NonNilUuid {
39
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40
0
        fmt::Debug::fmt(&Uuid::from(*self), f)
41
0
    }
42
}
43
44
impl fmt::Display for NonNilUuid {
45
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46
0
        fmt::Display::fmt(&Uuid::from(*self), f)
47
0
    }
48
}
49
50
impl PartialEq<Uuid> for NonNilUuid {
51
0
    fn eq(&self, other: &Uuid) -> bool {
52
0
        self.get() == *other
53
0
    }
54
}
55
56
impl PartialEq<NonNilUuid> for Uuid {
57
0
    fn eq(&self, other: &NonNilUuid) -> bool {
58
0
        *self == other.get()
59
0
    }
60
}
61
62
impl PartialOrd<Uuid> for NonNilUuid {
63
0
    fn partial_cmp(&self, other: &Uuid) -> Option<Ordering> {
64
0
        self.get().partial_cmp(other)
65
0
    }
66
}
67
68
impl PartialOrd<NonNilUuid> for Uuid {
69
0
    fn partial_cmp(&self, other: &NonNilUuid) -> Option<Ordering> {
70
0
        self.partial_cmp(&other.get())
71
0
    }
72
}
73
74
impl NonNilUuid {
75
    /// Creates a non-nil UUID if the value is non-nil.
76
0
    pub const fn new(uuid: Uuid) -> Option<Self> {
77
0
        match NonZeroU128::new(uuid.as_u128()) {
78
0
            Some(non_nil) => Some(NonNilUuid(non_nil)),
79
0
            None => None,
80
        }
81
0
    }
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
0
    pub const unsafe fn new_unchecked(uuid: Uuid) -> Self {
89
0
        NonNilUuid(unsafe { NonZeroU128::new_unchecked(uuid.as_u128()) })
90
0
    }
91
92
    /// Get the underlying [`Uuid`] value.
93
    #[inline]
94
0
    pub const fn get(self) -> Uuid {
95
0
        Uuid::from_u128(self.0.get())
96
0
    }
97
}
98
99
impl From<NonNilUuid> for Uuid {
100
    /// Converts a [`NonNilUuid`] back into a [`Uuid`].
101
    ///
102
    /// # Examples
103
    /// ```
104
    /// # use uuid::{NonNilUuid, Uuid};
105
    /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
106
    /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
107
    /// let uuid_again = Uuid::from(non_nil);
108
    ///
109
    /// assert_eq!(uuid, uuid_again);
110
    /// ```
111
0
    fn from(non_nil: NonNilUuid) -> Self {
112
0
        Uuid::from_u128(non_nil.0.get())
113
0
    }
114
}
115
116
impl TryFrom<Uuid> for NonNilUuid {
117
    type Error = Error;
118
119
    /// Attempts to convert a [`Uuid`] into a [`NonNilUuid`].
120
    ///
121
    /// # Examples
122
    /// ```
123
    /// # use uuid::{NonNilUuid, Uuid};
124
    /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
125
    /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
126
    /// ```
127
0
    fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
128
0
        NonZeroU128::new(uuid.as_u128())
129
0
            .map(Self)
130
0
            .ok_or(Error(ErrorKind::Nil))
131
0
    }
132
}
133
134
#[cfg(test)]
135
mod tests {
136
    use super::*;
137
138
    #[test]
139
    fn test_non_nil_with_option_size() {
140
        assert_eq!(
141
            std::mem::size_of::<Option<NonNilUuid>>(),
142
            std::mem::size_of::<Uuid>()
143
        );
144
    }
145
146
    #[test]
147
    fn test_non_nil() {
148
        let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
149
150
        assert_eq!(Uuid::from(NonNilUuid::try_from(uuid).unwrap()), uuid);
151
        assert_eq!(NonNilUuid::new(uuid).unwrap(), uuid);
152
        assert_eq!(unsafe { NonNilUuid::new_unchecked(uuid) }, uuid);
153
154
        assert!(NonNilUuid::try_from(Uuid::nil()).is_err());
155
        assert!(NonNilUuid::new(Uuid::nil()).is_none());
156
    }
157
158
    #[test]
159
    fn test_non_nil_formatting() {
160
        let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
161
        let non_nil = NonNilUuid::try_from(uuid).unwrap();
162
163
        assert_eq!(format!("{uuid}"), format!("{non_nil}"));
164
        assert_eq!(format!("{uuid:?}"), format!("{non_nil:?}"));
165
    }
166
167
    #[test]
168
    fn test_non_nil_ord() {
169
        let uuid1 = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
170
        let uuid2 = Uuid::from_u128(0x0123456789abcdef0123456789abcdf0);
171
172
        let non_nil1 = NonNilUuid::try_from(uuid1).unwrap();
173
        let non_nil2 = NonNilUuid::try_from(uuid2).unwrap();
174
175
        // Ordering between NonNilUuid instances
176
        assert!(non_nil1 < non_nil2);
177
        assert!(non_nil2 > non_nil1);
178
179
        // Ordering between NonNilUuid and Uuid
180
        assert!(non_nil1 < uuid2);
181
        assert!(non_nil2 > uuid1);
182
183
        // Ordering between Uuid and NonNilUuid
184
        assert!(uuid1 < non_nil2);
185
        assert!(uuid2 > non_nil1);
186
187
        // Equality
188
        let non_nil1_copy = NonNilUuid::try_from(uuid1).unwrap();
189
        assert_eq!(non_nil1, non_nil1_copy);
190
        assert!(non_nil1 <= non_nil1_copy);
191
        assert!(non_nil1 >= non_nil1_copy);
192
193
        // Equality between NonNilUuid and Uuid
194
        assert_eq!(non_nil1, uuid1);
195
        assert_eq!(uuid1, non_nil1);
196
        assert!(non_nil1 >= uuid1);
197
        assert!(non_nil1 <= uuid1);
198
        assert!(uuid1 >= non_nil1);
199
        assert!(uuid1 <= non_nil1);
200
    }
201
}