#![doc(html_root_url = "https://docs.rs/uuid-rs")]
mod name;
mod rand;
mod time;
use core::fmt;
use core::sync::atomic;
use std::time::SystemTime;
pub const UTC_EPOCH: u64 = 0x1B21_DD21_3814_000;
#[derive(Debug)]
pub struct Layout {
pub field_low: u32,
pub field_mid: u16,
pub field_high_and_version: u16,
pub clock_seq_high_and_reserved: u8,
pub clock_seq_low: u8,
pub node: [u8; 6],
}
impl Layout {
pub fn as_fields(&self) -> (u32, u16, u16, u16, u64) {
(
self.field_low,
self.field_mid,
self.field_high_and_version,
((self.clock_seq_high_and_reserved as u16) << 8) | self.clock_seq_low as u16,
(self.node[0] as u64) << 40
| (self.node[1] as u64) << 32
| (self.node[2] as u64) << 24
| (self.node[3] as u64) << 16
| (self.node[4] as u64) << 8
| (self.node[5] as u64),
)
}
pub fn as_bytes(&self) -> UUID {
UUID([
self.field_low.to_be_bytes()[0],
self.field_low.to_be_bytes()[1],
self.field_low.to_be_bytes()[2],
self.field_low.to_be_bytes()[3],
self.field_mid.to_be_bytes()[0],
self.field_mid.to_be_bytes()[1],
self.field_high_and_version.to_be_bytes()[0],
self.field_high_and_version.to_be_bytes()[1],
self.clock_seq_high_and_reserved,
self.clock_seq_low,
self.node[0],
self.node[1],
self.node[2],
self.node[3],
self.node[4],
self.node[5],
])
}
pub fn get_version(&self) -> Option<Version> {
match (self.field_high_and_version >> 12) & 0xf {
0x01 => Some(Version::TIME),
0x02 => Some(Version::DCE),
0x03 => Some(Version::MD5),
0x04 => Some(Version::RAND),
0x05 => Some(Version::SHA1),
_ => None,
}
}
pub fn get_variant(&self) -> Option<Variant> {
match (self.clock_seq_high_and_reserved >> 4) & 0xf {
0x00 => Some(Variant::NCS),
0x01 => Some(Variant::RFC),
0x02 => Some(Variant::MS),
0x03 => Some(Variant::FUT),
_ => None,
}
}
pub fn get_time(&self) -> u64 {
let t = ((self.field_high_and_version) as u64) << 48
| (self.field_mid as u64) << 32
| self.field_low as u64;
t.checked_sub(UTC_EPOCH).unwrap()
}
pub fn get_mac(&self) -> Node {
Node(self.node)
}
}
#[derive(Debug, Copy, Clone)]
pub enum Domain {
PERSON = 0,
GROUP,
ORG,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Variant {
NCS = 0,
RFC,
MS,
FUT,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Version {
TIME = 1,
DCE,
MD5,
RAND,
SHA1,
}
#[derive(Debug, Eq, PartialEq)]
pub struct Timestamp(u64);
impl Timestamp {
pub fn new() -> u64 {
let utc = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.checked_add(std::time::Duration::from_nanos(UTC_EPOCH))
.unwrap()
.as_nanos();
(utc & 0xffff_ffff_ffff_fff) as u64
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct UUID([u8; 16]);
impl UUID {
pub const NAMESPACE_DNS: Self = UUID([
0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
0xc8,
]);
pub const NAMESPACE_OID: Self = UUID([
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
0xc8,
]);
pub const NAMESPACE_URL: Self = UUID([
0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
0xc8,
]);
pub const NAMESPACE_X500: Self = UUID([
0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30,
0xc8,
]);
}
impl fmt::Display for UUID {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
self.0[0],
self.0[1],
self.0[2],
self.0[3],
self.0[4],
self.0[5],
self.0[6],
self.0[7],
self.0[8],
self.0[9],
self.0[10],
self.0[11],
self.0[12],
self.0[13],
self.0[14],
self.0[15],
)
}
}
pub struct ClockSeq(u16);
impl ClockSeq {
pub fn new(r: u16) -> u16 {
atomic::AtomicU16::new(r).fetch_add(1, atomic::Ordering::AcqRel)
}
}
pub struct Node([u8; 6]);
impl fmt::Display for Node {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
fmt,
"{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5],
)
}
}
#[cfg(any(
feature = "hash_md5",
feauture = "hash_sha1",
feauture = "random",
feauture = "mac"
))]
#[cfg(test)]
mod tests {
use super::*;
use regex::Regex;
fn is_valid(s: &str) -> bool {
let regex = Regex::new(
r"^(?i)(urn:uuid:)?[0-9a-f]{8}\-[0-9a-f]{4}\-[0-5]{1}[0-9a-f]{3}\-[0-9a-f]{4}\-[0-9a-f]{12}$",
);
regex.unwrap().is_match(s)
}
#[test]
fn test_node_format() {
let node = Node([00, 42, 53, 13, 19, 128]);
assert_eq!(format!("{}", node), "00-2a-35-0d-13-80");
assert_eq!(format!("{}", node).to_uppercase(), "00-2A-35-0D-13-80")
}
#[test]
fn test_is_valid_uuid() {
let uuid = [
v1!(),
v2!(Domain::PERSON),
v3!("any", UUID::NAMESPACE_URL),
v4!(),
v4!(),
v5!("any", UUID::NAMESPACE_DNS),
];
for id in uuid.iter() {
assert!(is_valid(id))
}
for id in uuid.iter() {
assert!(is_valid(&id.to_uppercase()))
}
}
}