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