use rand_core::CryptoRng;
use uuid::Uuid;
macro_rules! id_type {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct $name(Uuid);
impl $name {
pub fn random<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
let mut bytes = [0u8; 16];
rng.fill_bytes(&mut bytes);
Self(uuid::Builder::from_random_bytes(bytes).into_uuid())
}
#[must_use]
pub const fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
#[must_use]
pub const fn as_uuid(&self) -> &Uuid {
&self.0
}
#[must_use]
pub const fn into_uuid(self) -> Uuid {
self.0
}
}
impl From<Uuid> for $name {
fn from(uuid: Uuid) -> Self {
Self(uuid)
}
}
impl From<$name> for Uuid {
fn from(id: $name) -> Uuid {
id.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::str::FromStr for $name {
type Err = uuid::Error;
fn from_str(raw: &str) -> Result<Self, Self::Err> {
Uuid::parse_str(raw).map(Self)
}
}
};
}
id_type!(
UserId
);
id_type!(
SessionId
);
id_type!(
PasskeyId
);
id_type!(
CeremonyId
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn random_ids_are_v4_and_round_trip() -> Result<(), uuid::Error> {
let mut rng = crate::test_rng();
let id = UserId::random(&mut rng);
assert_eq!(id.as_uuid().get_version_num(), 4);
assert_eq!(id.to_string().parse::<UserId>()?, id);
assert_ne!(UserId::random(&mut rng), id);
Ok(())
}
}