Coverage Report

Created: 2025-07-01 06:28

/rust/registry/src/index.crates.io-6f17d22bba15001f/uuid-1.17.0/src/non_nil.rs
Line
Count
Source (jump to first uncovered line)
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(Copy, Clone, PartialEq, Eq, Hash)]
37
pub struct NonNilUuid(NonZeroU128);
38
39
impl fmt::Debug for NonNilUuid {
40
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41
0
        fmt::Debug::fmt(&Uuid::from(*self), f)
42
0
    }
43
}
44
45
impl fmt::Display for NonNilUuid {
46
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47
0
        fmt::Display::fmt(&Uuid::from(*self), f)
48
0
    }
49
}
50
51
impl PartialEq<Uuid> for NonNilUuid {
52
0
    fn eq(&self, other: &Uuid) -> bool {
53
0
        self.get() == *other
54
0
    }
55
}
56
57
impl PartialEq<NonNilUuid> for Uuid {
58
0
    fn eq(&self, other: &NonNilUuid) -> bool {
59
0
        *self == other.get()
60
0
    }
61
}
62
63
impl NonNilUuid {
64
    /// Creates a non-nil UUID if the value is non-nil.
65
0
    pub const fn new(uuid: Uuid) -> Option<Self> {
66
0
        match NonZeroU128::new(uuid.as_u128()) {
67
0
            Some(non_nil) => Some(NonNilUuid(non_nil)),
68
0
            None => None,
69
        }
70
0
    }
71
72
    /// Creates a non-nil without checking whether the value is non-nil. This results in undefined behavior if the value is nil.
73
    ///
74
    /// # Safety
75
    ///
76
    /// The value must not be nil.
77
0
    pub const unsafe fn new_unchecked(uuid: Uuid) -> Self {
78
0
        NonNilUuid(unsafe { NonZeroU128::new_unchecked(uuid.as_u128()) })
79
0
    }
80
81
    /// Get the underlying [`Uuid`] value.
82
    #[inline]
83
0
    pub const fn get(self) -> Uuid {
84
0
        Uuid::from_u128(self.0.get())
85
0
    }
86
}
87
88
impl From<NonNilUuid> for Uuid {
89
    /// Converts a [`NonNilUuid`] back into a [`Uuid`].
90
    ///
91
    /// # Examples
92
    /// ```
93
    /// # use std::convert::TryFrom;
94
    /// # use uuid::{NonNilUuid, Uuid};
95
    /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
96
    /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
97
    /// let uuid_again = Uuid::from(non_nil);
98
    ///
99
    /// assert_eq!(uuid, uuid_again);
100
    /// ```
101
0
    fn from(non_nil: NonNilUuid) -> Self {
102
0
        Uuid::from_u128(non_nil.0.get())
103
0
    }
104
}
105
106
impl TryFrom<Uuid> for NonNilUuid {
107
    type Error = Error;
108
109
    /// Attempts to convert a [`Uuid`] into a [`NonNilUuid`].
110
    ///
111
    /// # Examples
112
    /// ```
113
    /// # use std::convert::TryFrom;
114
    /// # use uuid::{NonNilUuid, Uuid};
115
    /// let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
116
    /// let non_nil = NonNilUuid::try_from(uuid).unwrap();
117
    /// ```
118
0
    fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
119
0
        NonZeroU128::new(uuid.as_u128())
120
0
            .map(Self)
121
0
            .ok_or(Error(ErrorKind::Nil))
122
0
    }
123
}
124
125
#[cfg(test)]
126
mod tests {
127
    use super::*;
128
129
    #[test]
130
    fn test_non_nil_with_option_size() {
131
        assert_eq!(
132
            std::mem::size_of::<Option<NonNilUuid>>(),
133
            std::mem::size_of::<Uuid>()
134
        );
135
    }
136
137
    #[test]
138
    fn test_non_nil() {
139
        let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
140
141
        assert_eq!(Uuid::from(NonNilUuid::try_from(uuid).unwrap()), uuid);
142
        assert_eq!(NonNilUuid::new(uuid).unwrap(), uuid);
143
        assert_eq!(unsafe { NonNilUuid::new_unchecked(uuid) }, uuid);
144
145
        assert!(NonNilUuid::try_from(Uuid::nil()).is_err());
146
        assert!(NonNilUuid::new(Uuid::nil()).is_none());
147
    }
148
149
    #[test]
150
    fn test_non_nil_formatting() {
151
        let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
152
        let non_nil = NonNilUuid::try_from(uuid).unwrap();
153
154
        assert_eq!(format!("{uuid}"), format!("{non_nil}"));
155
        assert_eq!(format!("{uuid:?}"), format!("{non_nil:?}"));
156
    }
157
}