From 402a8be60c72779194961a3f0ca193e5b48fa393 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 14 Jan 2025 19:15:53 +1000 Subject: [PATCH] fill in a few non-nil methods --- src/error.rs | 3 +++ src/lib.rs | 4 ++-- src/non_nil.rs | 52 ++++++++++++++++++++++++++++++++++++++---------- src/timestamp.rs | 12 ++++++++--- src/v7.rs | 2 +- 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/error.rs b/src/error.rs index 30e0175..7a65c2d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,6 +30,8 @@ pub(crate) enum ErrorKind { }, /// The input was not a valid UTF8 string InvalidUTF8, + /// The UUID is nil. + Nil, /// Some other error occurred. Other, } @@ -158,6 +160,7 @@ impl fmt::Display for Error { ) } ErrorKind::InvalidUTF8 => write!(f, "non-UTF8 input"), + ErrorKind::Nil => write!(f, "the UUID is nil"), ErrorKind::Other => write!(f, "failed to parse a UUID"), } } diff --git a/src/lib.rs b/src/lib.rs index 385cd6c..1c30c38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,10 +227,10 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned}; mod builder; mod error; +mod non_nil; mod parser; pub mod fmt; -pub mod non_nil; pub mod timestamp; pub use timestamp::{context::NoContext, ClockSequence, Timestamp}; @@ -282,7 +282,7 @@ pub mod __macro_support { use crate::std::convert; -pub use crate::{builder::Builder, error::Error}; +pub use crate::{builder::Builder, error::Error, non_nil::NonNilUuid}; /// A 128-bit (16 byte) buffer containing the UUID. /// diff --git a/src/non_nil.rs b/src/non_nil.rs index 6642a49..71db073 100644 --- a/src/non_nil.rs +++ b/src/non_nil.rs @@ -4,12 +4,28 @@ use core::convert::TryFrom; use std::{fmt, num::NonZeroU128}; -use crate::Uuid; +use crate::{ + error::{Error, ErrorKind}, + Uuid, +}; /// A UUID that is guaranteed not to be the nil UUID. /// /// This is useful for representing optional UUIDs more efficiently, as `Option` /// 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`] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NonNilUuid(NonZeroU128); @@ -20,7 +36,24 @@ impl fmt::Display for NonNilUuid { } impl NonNilUuid { - /// Returns the underlying `Uuid`. + /// Creates a non-nil UUID if the value is non-nil. + pub const fn new(uuid: Uuid) -> Option { + 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()) @@ -47,7 +80,7 @@ impl From for Uuid { } impl TryFrom for NonNilUuid { - type Error = &'static str; + type Error = Error; /// Attempts to convert a [`Uuid`] into a [`NonNilUuid`]. /// @@ -62,7 +95,7 @@ impl TryFrom for NonNilUuid { fn try_from(uuid: Uuid) -> Result { NonZeroU128::new(uuid.as_u128()) .map(Self) - .ok_or("Attempted to convert nil Uuid to NonNilUuid") + .ok_or(Error(ErrorKind::Nil)) } } @@ -81,13 +114,12 @@ mod tests { #[test] fn test_non_nil() { let uuid = Uuid::from_u128(0x0123456789abcdef0123456789abcdef); - let nn_uuid = NonNilUuid::try_from(uuid); - assert!(nn_uuid.is_ok()); - assert_eq!(Uuid::from(nn_uuid.unwrap()), uuid); + assert_eq!(Uuid::from(NonNilUuid::try_from(uuid).unwrap()), uuid); + assert_eq!(NonNilUuid::new(uuid).unwrap().get(), uuid); + assert_eq!(unsafe { NonNilUuid::new_unchecked(uuid) }.get(), uuid); - let nil_uuid = Uuid::nil(); - let nn_uuid = NonNilUuid::try_from(nil_uuid); - assert!(nn_uuid.is_err()); + assert!(NonNilUuid::try_from(Uuid::nil()).is_err()); + assert!(NonNilUuid::new(Uuid::nil()).is_none()); } } diff --git a/src/timestamp.rs b/src/timestamp.rs index 56262f1..3bf9e9b 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -128,7 +128,7 @@ impl Timestamp { /// /// If conversion from the internal timestamp format to ticks would overflow /// then it will wrap. - /// + /// /// If the internal counter is wider than 14 bits then it will be truncated to 14 bits. pub const fn to_gregorian(&self) -> (u64, u16) { ( @@ -165,7 +165,10 @@ impl Timestamp { #[doc(hidden)] impl Timestamp { - #[deprecated(since = "1.11.1", note = "use `Timestamp::from_gregorian(ticks, counter)`")] + #[deprecated( + since = "1.11.1", + note = "use `Timestamp::from_gregorian(ticks, counter)`" + )] pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self { Timestamp::from_gregorian(ticks, counter) } @@ -175,7 +178,10 @@ impl Timestamp { self.to_gregorian() } - #[deprecated(since = "1.2.0", note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`")] + #[deprecated( + since = "1.2.0", + note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`" + )] pub const fn to_unix_nanos(&self) -> u32 { panic!("`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`") } diff --git a/src/v7.rs b/src/v7.rs index c2b4a22..d55cf94 100644 --- a/src/v7.rs +++ b/src/v7.rs @@ -31,7 +31,7 @@ impl Uuid { /// # Examples /// /// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit - /// random number. When supplied as such, the data will be combined + /// random number. When supplied as such, the data will be combined /// to ensure uniqueness and sortability at millisecond granularity. /// /// ```rust