use std::str::FromStr;
pub use const_uuid_proc_macros::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ConstUuidError {
err: &'static str,
}
impl ConstUuidError {
pub fn new(err: &'static str) -> Self {
Self { err }
}
}
impl std::fmt::Display for ConstUuidError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.err)
}
}
impl std::error::Error for ConstUuidError {}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ConstUuid(pub u128);
impl Default for ConstUuid {
fn default() -> Self {
ConstUuid(0)
}
}
impl FromStr for ConstUuid {
type Err = ConstUuidError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.len() != 36 {
return Err(ConstUuidError::new("Invalid UUID length"));
}
let mut bytes = [0u8; 16];
let mut i = 0;
for (j, c) in s.chars().enumerate() {
if j == 8 || j == 13 || j == 18 || j == 23 {
if c != '-' {
return Err(ConstUuidError::new("Invalid UUID format"));
}
continue;
}
if c.is_ascii_hexdigit() {
let b = c.to_digit(16).unwrap() as u8;
if i % 2 == 0 {
bytes[i / 2] = b << 4;
} else {
bytes[i / 2] |= b;
}
i += 1;
} else {
return Err(ConstUuidError::new("Invalid UUID format"));
}
}
Ok(ConstUuid(u128::from_be_bytes(bytes)))
}
}
impl std::fmt::Display for ConstUuid {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let bytes = self.0.to_be_bytes();
let string = bytes.iter().map(|b| format!("{:02x}", b)).collect::<String>();
write!(f, "{}-{}-{}-{}-{}", &string[0..8], &string[8..12], &string[12..16], &string[16..20], &string[20..32])
}
}
impl std::fmt::Debug for ConstUuid {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "ConstUuid({})", self)
}
}
impl ConstUuid {
pub const NIL: ConstUuid = ConstUuid(0);
pub fn to_u128(&self) -> u128 {
self.0
}
pub fn to_bytes(&self) -> [u8; 16] {
self.0.to_be_bytes()
}
pub fn is_nil(&self) -> bool {
self.0 == 0
}
}
#[macro_export]
macro_rules! const_uuid {
($uuid:expr) => {
$crate::ConstUuid($crate::const_uuid_u128!($uuid))
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn default() {
let uuid = ConstUuid::default();
assert_eq!(uuid.to_u128(), 0);
let parsed = const_uuid!("00000000-0000-0000-0000-000000000000");
assert_eq!(uuid, parsed);
}
#[test]
fn macro_vs_parse() {
let uuid = const_uuid!("01234567-89ab-cdef-0123-456789abcdef");
let parsed = ConstUuid::from_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
assert_eq!(uuid, parsed);
}
#[test]
fn parse_and_back() {
let uuid = ConstUuid::from_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
assert_eq!(uuid.to_string(), "01234567-89ab-cdef-0123-456789abcdef");
}
}