use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpectralUuid([u8; 16]);
impl SpectralUuid {
pub const EMPTY: Self = SpectralUuid([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40,
]);
pub fn from_parts(active: u64, content_hash: &[u8; 32]) -> Self {
let active_48 = active & 0x0000_FFFF_FFFF_FFFF;
let active_be = active_48.to_be_bytes(); let mut bytes = [0u8; 16];
bytes[0..6].copy_from_slice(&active_be[2..8]);
bytes[6..16].copy_from_slice(&content_hash[0..10]);
SpectralUuid(bytes)
}
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
SpectralUuid(bytes)
}
pub const fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
pub fn active(&self) -> u64 {
let mut be = [0u8; 8];
be[2..8].copy_from_slice(&self.0[0..6]);
u64::from_be_bytes(be)
}
pub fn dark(&self) -> [u8; 10] {
let mut out = [0u8; 10];
out.copy_from_slice(&self.0[6..16]);
out
}
pub fn parse(s: &str) -> Result<Self, ParseError> {
let bytes = s.as_bytes();
if bytes.len() != 36 {
return Err(ParseError::WrongLength(bytes.len()));
}
for pos in [8usize, 13, 18, 23] {
if bytes[pos] != b'-' {
return Err(ParseError::MissingHyphen(pos));
}
}
let mut out = [0u8; 16];
let hex_positions: [(usize, usize); 16] = [
(0, 2),
(2, 4),
(4, 6),
(6, 8),
(9, 11),
(11, 13),
(14, 16),
(16, 18),
(19, 21),
(21, 23),
(24, 26),
(26, 28),
(28, 30),
(30, 32),
(32, 34),
(34, 36),
];
for (i, (lo, hi)) in hex_positions.iter().enumerate() {
let hi_n = hex_digit(bytes[*lo]).ok_or(ParseError::NonHexDigit(*lo))?;
let lo_n = hex_digit(bytes[*lo + 1]).ok_or(ParseError::NonHexDigit(*lo + 1))?;
debug_assert_eq!(*hi, *lo + 2);
out[i] = (hi_n << 4) | lo_n;
}
Ok(SpectralUuid(out))
}
}
impl fmt::Display for SpectralUuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let b = &self.0;
write!(
f,
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
b[0], b[1], b[2], b[3],
b[4], b[5],
b[6], b[7],
b[8], b[9],
b[10], b[11], b[12], b[13], b[14], b[15],
)
}
}
impl fmt::Debug for SpectralUuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SpectralUuid({self})")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseError {
WrongLength(usize),
MissingHyphen(usize),
NonHexDigit(usize),
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::WrongLength(n) => {
write!(f, "SpectralUuid::parse: wrong length {n} (expected 36)")
}
ParseError::MissingHyphen(pos) => {
write!(f, "SpectralUuid::parse: missing hyphen at position {pos}")
}
ParseError::NonHexDigit(pos) => {
write!(f, "SpectralUuid::parse: non-hex digit at position {pos}")
}
}
}
}
impl std::error::Error for ParseError {}
fn hex_digit(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_is_const() {
const _E: SpectralUuid = SpectralUuid::EMPTY;
}
#[test]
fn hex_digit_round_trips() {
for n in 0u8..=15 {
let c = if n < 10 { b'0' + n } else { b'a' + n - 10 };
assert_eq!(hex_digit(c), Some(n));
}
assert_eq!(hex_digit(b' '), None);
assert_eq!(hex_digit(b'g'), None);
}
#[test]
fn display_is_36_chars() {
let s = format!("{}", SpectralUuid::EMPTY);
assert_eq!(s.len(), 36);
}
}