use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
struct SplitMix64(u64);
impl SplitMix64 {
fn next(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
}
fn seed_mix() -> u64 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let pid = std::process::id() as u64;
let ctr = COUNTER.fetch_add(1, Ordering::Relaxed);
nanos ^ pid.rotate_left(17) ^ ctr.rotate_left(31)
}
pub fn uuid_v7() -> String {
let ms: u64 = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let mut sm = SplitMix64(seed_mix());
let r1 = sm.next();
let r2 = sm.next();
let mut bytes = [0u8; 16];
bytes[0..6].copy_from_slice(&ms.to_be_bytes()[2..8]);
bytes[6] = ((ms >> 8) & 0x0f) as u8 | 0x70;
bytes[7] = (ms & 0xff) as u8;
bytes[8] = ((r1 & 0x3f) as u8) | 0x80;
bytes[9] = ((r1 >> 8) & 0xff) as u8;
bytes[10] = ((r1 >> 16) & 0xff) as u8;
bytes[11] = ((r1 >> 24) & 0xff) as u8;
bytes[12] = ((r1 >> 32) & 0xff) as u8;
bytes[13] = ((r1 >> 40) & 0xff) as u8;
bytes[14] = (r2 & 0xff) as u8;
bytes[15] = ((r2 >> 8) & 0xff) as u8;
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3],
bytes[4], bytes[5],
bytes[6], bytes[7],
bytes[8], bytes[9],
bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
)
}
pub fn is_canonical_uuid_v7(id: &str) -> bool {
if id.len() != 36 {
return false;
}
let bytes = id.as_bytes();
bytes[8] == b'-'
&& bytes[13] == b'-'
&& bytes[18] == b'-'
&& bytes[23] == b'-'
&& bytes[14] == b'7'
&& matches!(bytes[19], b'8' | b'9' | b'a' | b'b')
}
pub fn message_id(row_seq: i64) -> String {
format!("message-{row_seq}")
}
pub fn tool_call_origin_id(call_id: &str, row_seq: i64) -> String {
format!("tool_call_origin:{call_id}:{row_seq}")
}
pub fn tool_result_id(call_id: &str, row_seq: i64) -> String {
format!("tool:{call_id}:{row_seq}")
}
pub fn assistant_message_id(row_seq: i64) -> String {
format!("assistant:{row_seq}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uuid_v7_has_canonical_version_and_variant() {
let id = uuid_v7();
assert_eq!(id.len(), 36);
assert!(is_canonical_uuid_v7(&id), "{id} not v7");
}
#[test]
fn uuid_v7_is_lexicographically_sortable_by_minting_time() {
let a = uuid_v7();
std::thread::sleep(std::time::Duration::from_millis(2));
let b = uuid_v7();
assert!(a < b, "expected {a} < {b}");
}
#[test]
fn sequential_mints_differ_in_their_random_tail() {
let a = uuid_v7();
let b = uuid_v7();
assert_ne!(a, b);
}
#[test]
fn message_id_derives_from_row_seq() {
assert_eq!(message_id(7), "message-7");
assert_eq!(tool_call_origin_id("tc1", 11), "tool_call_origin:tc1:11");
assert_eq!(tool_result_id("tc1", 11), "tool:tc1:11");
assert_eq!(assistant_message_id(11), "assistant:11");
}
#[test]
fn is_canonical_uuid_v7_only_accepts_v7() {
assert!(is_canonical_uuid_v7("01920000-0000-7000-8000-000000000000"));
assert!(!is_canonical_uuid_v7(
"01920000-0000-4000-8000-000000000000"
));
assert!(!is_canonical_uuid_v7("not-a-uuid"));
assert!(!is_canonical_uuid_v7(""));
}
}