mirror of
https://github.com/uuid-rs/uuid.git
synced 2025-09-30 06:21:02 +00:00
Merge pull request #779 from ab22593k/main
feat: Add `NonZeroUuid` type for optimized `Option<Uuid>` representation
This commit is contained in:
commit
618e8174a4
28
src/external/arbitrary_support.rs
vendored
28
src/external/arbitrary_support.rs
vendored
@ -1,4 +1,8 @@
|
||||
use crate::{std::convert::TryInto, Builder, Uuid};
|
||||
use crate::{
|
||||
non_nil::NonNilUuid,
|
||||
std::convert::{TryFrom, TryInto},
|
||||
Builder, Uuid,
|
||||
};
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
|
||||
@ -16,6 +20,16 @@ impl Arbitrary<'_> for Uuid {
|
||||
(16, Some(16))
|
||||
}
|
||||
}
|
||||
impl arbitrary::Arbitrary<'_> for NonNilUuid {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
|
||||
let uuid = Uuid::arbitrary(u)?;
|
||||
Self::try_from(uuid).map_err(|_| arbitrary::Error::NotEnoughData)
|
||||
}
|
||||
|
||||
fn size_hint(_: usize) -> (usize, Option<usize>) {
|
||||
(16, Some(16))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -42,4 +56,16 @@ mod tests {
|
||||
|
||||
assert!(uuid.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbitrary_non_nil() {
|
||||
let mut bytes = Unstructured::new(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
let non_nil_uuid = NonNilUuid::arbitrary(&mut bytes).unwrap();
|
||||
let uuid: Uuid = non_nil_uuid.into();
|
||||
|
||||
assert_eq!(Some(Version::Random), uuid.get_version());
|
||||
assert_eq!(Variant::RFC4122, uuid.get_variant());
|
||||
assert!(!uuid.is_nil());
|
||||
}
|
||||
}
|
||||
|
32
src/external/serde_support.rs
vendored
32
src/external/serde_support.rs
vendored
@ -10,8 +10,10 @@
|
||||
// except according to those terms.
|
||||
|
||||
use crate::{
|
||||
convert::TryFrom,
|
||||
error::*,
|
||||
fmt::{Braced, Hyphenated, Simple, Urn},
|
||||
non_nil::NonNilUuid,
|
||||
std::fmt,
|
||||
Uuid,
|
||||
};
|
||||
@ -30,6 +32,15 @@ impl Serialize for Uuid {
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for NonNilUuid {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
Uuid::from(*self).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Hyphenated {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(self.encode_lower(&mut Uuid::encode_buffer()))
|
||||
@ -127,6 +138,17 @@ impl<'de> Deserialize<'de> for Uuid {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for NonNilUuid {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let uuid = Uuid::deserialize(deserializer)?;
|
||||
|
||||
NonNilUuid::try_from(uuid).map_err(|_| de::Error::custom("Uuid cannot be nil"))
|
||||
}
|
||||
}
|
||||
|
||||
enum ExpectedFormat {
|
||||
Simple,
|
||||
Braced,
|
||||
@ -732,4 +754,14 @@ mod serde_tests {
|
||||
"UUID parsing failed: invalid length: expected 16 bytes, found 11",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_non_nil_uuid() {
|
||||
let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4";
|
||||
let uuid = Uuid::parse_str(uuid_str).unwrap();
|
||||
let non_nil_uuid = NonNilUuid::try_from(uuid).unwrap();
|
||||
|
||||
serde_test::assert_ser_tokens(&non_nil_uuid.readable(), &[Token::Str(uuid_str)]);
|
||||
serde_test::assert_de_tokens(&non_nil_uuid.readable(), &[Token::Str(uuid_str)]);
|
||||
}
|
||||
}
|
||||
|
13
src/external/slog_support.rs
vendored
13
src/external/slog_support.rs
vendored
@ -9,7 +9,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::Uuid;
|
||||
use crate::{non_nil::NonNilUuid, Uuid};
|
||||
|
||||
impl slog::Value for Uuid {
|
||||
fn serialize(
|
||||
@ -22,6 +22,17 @@ impl slog::Value for Uuid {
|
||||
}
|
||||
}
|
||||
|
||||
impl slog::Value for NonNilUuid {
|
||||
fn serialize(
|
||||
&self,
|
||||
record: &slog::Record<'_>,
|
||||
key: slog::Key,
|
||||
serializer: &mut dyn slog::Serializer,
|
||||
) -> Result<(), slog::Error> {
|
||||
Uuid::from(*self).serialize(record, key, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::new;
|
||||
|
@ -223,13 +223,14 @@ extern crate std;
|
||||
extern crate core as std;
|
||||
|
||||
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
|
||||
use zerocopy::{IntoBytes, FromBytes, Immutable, KnownLayout, Unaligned};
|
||||
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||
|
||||
mod builder;
|
||||
mod error;
|
||||
mod parser;
|
||||
|
||||
pub mod fmt;
|
||||
pub mod non_nil;
|
||||
pub mod timestamp;
|
||||
|
||||
pub use timestamp::{context::NoContext, ClockSequence, Timestamp};
|
||||
|
93
src/non_nil.rs
Normal file
93
src/non_nil.rs
Normal file
@ -0,0 +1,93 @@
|
||||
//! 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::Uuid;
|
||||
|
||||
/// A UUID that is guaranteed not to be the nil UUID.
|
||||
///
|
||||
/// This is useful for representing optional UUIDs more efficiently, as `Option<NonNilUuid>`
|
||||
/// takes up the same space as `Uuid`.
|
||||
#[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 NonNilUuid {
|
||||
/// Returns the underlying `Uuid`.
|
||||
#[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 uuid::{non_nil::NonNilUuid, Uuid};
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// 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 = &'static str;
|
||||
|
||||
/// Attempts to convert a [`Uuid`] into a [`NonNilUuid`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use uuid::{non_nil::NonNilUuid, Uuid};
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// 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("Attempted to convert nil Uuid to NonNilUuid")
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
let nn_uuid = NonNilUuid::try_from(uuid);
|
||||
|
||||
assert!(nn_uuid.is_ok());
|
||||
assert_eq!(Uuid::from(nn_uuid.unwrap()), uuid);
|
||||
|
||||
let nil_uuid = Uuid::nil();
|
||||
let nn_uuid = NonNilUuid::try_from(nil_uuid);
|
||||
assert!(nn_uuid.is_err());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user