use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::LazyLock;
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
static MACHINE_ID: LazyLock<u16> = LazyLock::new(|| {
let pid = std::process::id();
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as u32;
((pid ^ timestamp) & 0xFFFF) as u16
});
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TraceId(String);
impl TraceId {
#[inline]
fn get_machine_id() -> u16 {
*MACHINE_ID
}
#[inline]
pub fn new() -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
let machine_id = Self::get_machine_id();
let random_part = fastrand::u32(..);
let high_64 = ((timestamp & 0xFFFFFFFFFFFF) << 16) | (machine_id as u64);
let low_64 = (counter & 0xFFFFFFFF) << 32 | (random_part as u64);
let id = format!("{high_64:016x}{low_64:016x}");
Self(id)
}
#[inline]
pub fn from_string_validated(id: &str) -> Option<Self> {
if id.len() != 32 {
return None;
}
if !Self::is_valid_hex_bytes(id.as_bytes()) {
return None;
}
if id == "00000000000000000000000000000000" {
return None;
}
Some(Self(id.to_string()))
}
#[inline]
fn is_valid_hex_bytes(bytes: &[u8]) -> bool {
bytes
.iter()
.all(|&b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
}
#[inline]
pub fn from_string_unchecked(id: &str) -> Self {
Self(id.to_string())
}
#[cfg(test)]
pub(crate) fn from_string(id: &str) -> Self {
Self(id.to_string())
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for TraceId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Default for TraceId {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_trace_id_creation() {
let trace_id = TraceId::new();
let id_str = trace_id.as_str();
assert_eq!(id_str.len(), 32);
assert!(id_str
.chars()
.all(|c| c.is_ascii_hexdigit() && (c.is_ascii_digit() || c.is_ascii_lowercase())));
assert_ne!(id_str, "00000000000000000000000000000000");
}
#[test]
fn test_trace_id_from_string() {
let id_str = "0af7651916cd43dd8448eb211c80319c";
let trace_id = TraceId::from_string(id_str);
assert_eq!(trace_id.as_str(), id_str);
}
#[test]
fn test_trace_id_display() {
let trace_id = TraceId::from_string("0af7651916cd43dd8448eb211c80319c");
assert_eq!(format!("{trace_id}"), "0af7651916cd43dd8448eb211c80319c");
}
#[test]
fn test_trace_id_debug() {
let trace_id = TraceId::from_string("0af7651916cd43dd8448eb211c80319c");
let debug_str = format!("{trace_id:?}");
assert!(debug_str.contains("0af7651916cd43dd8448eb211c80319c"));
}
#[test]
fn test_from_string_validated() {
let valid_id = "0af7651916cd43dd8448eb211c80319c";
assert_eq!(
TraceId::from_string_validated(valid_id),
Some(TraceId(valid_id.to_string()))
);
assert_eq!(TraceId::from_string_validated("short"), None);
assert_eq!(
TraceId::from_string_validated("toolongtraceidentifierthatexceeds32chars"),
None
);
assert_eq!(
TraceId::from_string_validated("0AF7651916CD43DD8448EB211C80319C"),
None
);
assert_eq!(
TraceId::from_string_validated("0af7651916cd43dd8448eb211c80319g"),
None
);
assert_eq!(
TraceId::from_string_validated("00000000000000000000000000000000"),
None
);
}
#[test]
fn test_trace_id_uniqueness() {
let mut ids = std::collections::HashSet::new();
for _ in 0..1000 {
let trace_id = TraceId::new();
assert!(
ids.insert(trace_id.as_str().to_string()),
"Generated duplicate trace ID"
);
}
}
#[test]
fn test_w3c_traceparent_compatibility() {
let trace_id = TraceId::new();
let parent_id = "b7ad6b7169203331";
let trace_flags = "01";
let traceparent = format!("00-{}-{}-{}", trace_id.as_str(), parent_id, trace_flags);
let parts: Vec<&str> = traceparent.split('-').collect();
assert_eq!(parts.len(), 4);
assert_eq!(parts[0], "00"); assert_eq!(parts[1].len(), 32); assert_eq!(parts[2].len(), 16); assert_eq!(parts[3].len(), 2); }
#[test]
fn test_additional_impls() {
let default_id = TraceId::default();
assert_eq!(default_id.as_str().len(), 32);
assert!(TraceId::from_string_validated(default_id.as_str()).is_some());
let id1 = TraceId::new();
let id2 = id1.clone();
let id3 = TraceId::new();
assert_eq!(id1, id2, "Cloned ID should be equal to the original");
assert_ne!(id1, id3, "Different IDs should not be equal");
let invalid_str = "this-is-not-a-valid-id";
let unchecked_id = TraceId::from_string_unchecked(invalid_str);
assert_eq!(unchecked_id.as_str(), invalid_str);
assert!(TraceId::from_string_validated(invalid_str).is_none());
}
}