mirror of
https://github.com/uuid-rs/uuid.git
synced 2025-10-02 07:20:40 +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};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
@ -16,6 +20,16 @@ impl Arbitrary<'_> for Uuid {
|
|||||||
(16, Some(16))
|
(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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@ -42,4 +56,16 @@ mod tests {
|
|||||||
|
|
||||||
assert!(uuid.is_err());
|
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.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
convert::TryFrom,
|
||||||
error::*,
|
error::*,
|
||||||
fmt::{Braced, Hyphenated, Simple, Urn},
|
fmt::{Braced, Hyphenated, Simple, Urn},
|
||||||
|
non_nil::NonNilUuid,
|
||||||
std::fmt,
|
std::fmt,
|
||||||
Uuid,
|
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 {
|
impl Serialize for Hyphenated {
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
serializer.serialize_str(self.encode_lower(&mut Uuid::encode_buffer()))
|
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 {
|
enum ExpectedFormat {
|
||||||
Simple,
|
Simple,
|
||||||
Braced,
|
Braced,
|
||||||
@ -732,4 +754,14 @@ mod serde_tests {
|
|||||||
"UUID parsing failed: invalid length: expected 16 bytes, found 11",
|
"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
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::Uuid;
|
use crate::{non_nil::NonNilUuid, Uuid};
|
||||||
|
|
||||||
impl slog::Value for Uuid {
|
impl slog::Value for Uuid {
|
||||||
fn serialize(
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::new;
|
use crate::tests::new;
|
||||||
|
@ -223,13 +223,14 @@ extern crate std;
|
|||||||
extern crate core as std;
|
extern crate core as std;
|
||||||
|
|
||||||
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
|
#[cfg(all(uuid_unstable, feature = "zerocopy"))]
|
||||||
use zerocopy::{IntoBytes, FromBytes, Immutable, KnownLayout, Unaligned};
|
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
mod error;
|
mod error;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
|
pub mod non_nil;
|
||||||
pub mod timestamp;
|
pub mod timestamp;
|
||||||
|
|
||||||
pub use timestamp::{context::NoContext, ClockSequence, 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