use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
const ENCODING: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
pub fn ulid_new() -> u128 {
static SEQ: AtomicU64 = AtomicU64::new(0);
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default();
let ts_ms = (now.as_millis() as u128) & 0xFFFF_FFFF_FFFFu128;
let seq = SEQ.fetch_add(1, Ordering::Relaxed) as u128;
let nanos = now.subsec_nanos() as u128;
let rand_80 = (seq
.wrapping_mul(6_364_136_223_846_793_005u128)
.wrapping_add(nanos.wrapping_mul(0x9e37_79b9_7f4a_7c15u128)))
& ((1u128 << 80) - 1);
(ts_ms << 80) | rand_80
}
pub fn ulid_encode(v: u128) -> String {
let mut buf = [0u8; 26];
let mut n = v;
for i in (0..26).rev() {
buf[i] = ENCODING[(n & 0x1F) as usize];
n >>= 5;
}
unsafe { String::from_utf8_unchecked(buf.to_vec()) }
}
pub fn ulid_decode(s: &str) -> Result<u128, String> {
if s.len() != 26 {
return Err(format!("ULID must be 26 characters, got {}", s.len()));
}
let mut v: u128 = 0;
for c in s.chars() {
let digit: u128 = match c.to_ascii_uppercase() {
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14,
'F' => 15, 'G' => 16, 'H' => 17, 'J' => 18, 'K' => 19,
'M' => 20, 'N' => 21, 'P' => 22, 'Q' => 23, 'R' => 24,
'S' => 25, 'T' => 26, 'V' => 27, 'W' => 28, 'X' => 29,
'Y' => 30, 'Z' => 31,
other => return Err(format!("invalid ULID character: '{}'", other)),
};
v = (v << 5) | digit;
}
Ok(v)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct NodeId(pub u128);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct EdgeId(pub u128);
impl std::fmt::Display for NodeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", ulid_encode(self.0))
}
}
impl std::fmt::Display for EdgeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", ulid_encode(self.0))
}
}