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