use mac_address as MAC;
use core::fmt;
use core::sync::atomic;
use std::time::{self, SystemTime};
use crate::*;
pub const UTC_EPOCH: u64 = 0x1B21_DD21_3814_000;
pub enum Domain {
PERSON = 0,
GROUP,
ORG,
}
impl UUID {
pub fn v1() -> Layout {
let utc = Timestamp::new();
let clock_seq = Self::clock_seq_high_and_reserved(1);
Layout {
time_low: ((utc & 0xffff_ffff) as u32),
time_mid: ((utc >> 32 & 0xffff) as u16),
time_high_and_version: (utc >> 48 & 0xfff) as u16 | (Version::TIME as u16) << 12,
clock_seq_high_and_reserved: clock_seq.0,
clock_seq_low: clock_seq.1,
node: Self::mac_address(),
}
}
pub fn v2(d: Domain) -> Layout {
let utc = Timestamp::new();
Layout {
time_low: (utc & 0xffff_ffff) as u32,
time_mid: ((utc >> 32 & 0xffff) as u16),
time_high_and_version: (utc >> 48 & 0xfff) as u16 | (Version::DCE as u16) << 12,
clock_seq_high_and_reserved: Self::clock_seq_high_and_reserved(1).0,
clock_seq_low: d as u8,
node: Self::mac_address(),
}
}
fn clock_seq_high_and_reserved(s: u8) -> (u8, u8) {
let clock_seq = ClockSeq::new(rand::random::<u16>());
(
((clock_seq >> 8) & 0xf) as u8 | s << 4,
(clock_seq & 0xff) as u8,
)
}
fn mac_address() -> [u8; 6] {
MAC::get_mac_address().unwrap().unwrap().bytes()
}
}
impl Layout {
pub fn get_time(&self) -> Timestamp {
let time = (self.time_high_and_version as u64 & 0xfff) << 48
| (self.time_mid as u64) << 32
| self.time_low as u64;
Timestamp(time)
}
}
#[derive(Debug)]
pub struct Timestamp(pub u64);
impl Timestamp {
pub fn new() -> u64 {
let nano = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.checked_add(std::time::Duration::from_nanos(UTC_EPOCH))
.unwrap()
.as_nanos();
(nano & 0xffff_ffff_ffff_ffff) as u64
}
pub fn duration(&self) -> time::Duration {
time::Duration::from_nanos(self.0)
}
}
pub struct Node(pub [u8; 6]);
impl fmt::LowerHex 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],
)
}
}
impl fmt::UpperHex 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],
)
}
}
pub struct ClockSeq(u16);
impl ClockSeq {
pub fn new(r: u16) -> u16 {
atomic::AtomicU16::new(r).fetch_add(1, atomic::Ordering::SeqCst)
}
}
#[macro_export]
macro_rules! uuid_v1 {
() => {
format!("{:x}", $crate::UUID::v1().as_bytes())
};
}
#[macro_export]
macro_rules! uuid_v2 {
($domain:expr) => {
format!("{:x}", $crate::UUID::v2($domain).as_bytes())
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_v1() {
let uuid = UUID::v1();
assert_eq!(uuid.get_version(), Some(Version::TIME));
assert_eq!(uuid.get_variant(), Some(Variant::RFC));
assert!(UUID::is_valid(&format!("{:x}", uuid.as_bytes())));
assert!(UUID::is_valid(&format!("{:X}", uuid.as_bytes())));
}
#[test]
fn test_v2() {
let uuid = UUID::v2(Domain::PERSON);
assert_eq!(uuid.get_version(), Some(Version::DCE));
assert_eq!(uuid.get_variant(), Some(Variant::RFC));
assert!(UUID::is_valid(&format!("{:x}", uuid.as_bytes())));
assert!(UUID::is_valid(&format!("{:X}", uuid.as_bytes())));
}
#[test]
fn test_node() {
let node = Node([00, 42, 53, 13, 19, 128]);
assert_eq!(format!("{:x}", node), "00-2a-35-0d-13-80");
assert_eq!(format!("{:X}", node), "00-2A-35-0D-13-80")
}
#[test]
fn test_from_macro() {
assert!(UUID::is_valid(&uuid_v1!()));
assert!(UUID::is_valid(&uuid_v2!(Domain::PERSON)));
}
}