pub const TRACEPARENT: &str = "traceparent";
const VERSION: &str = "00";
const SAMPLED: &str = "01";
const ZERO_TRACE: &str = "00000000000000000000000000000000";
const ZERO_SPAN: &str = "0000000000000000";
pub fn format(trace_id: &str, span_id: &str) -> Option<String> {
if valid_trace_id(trace_id) && valid_span_id(span_id) {
Some(format!("{VERSION}-{trace_id}-{span_id}-{SAMPLED}"))
} else {
None
}
}
pub fn parse(traceparent: &str) -> Option<(String, String)> {
let parts: Vec<&str> = traceparent.trim().split('-').collect();
if parts.len() >= 4 && valid_trace_id(parts[1]) && valid_span_id(parts[2]) {
Some((parts[1].to_string(), parts[2].to_string()))
} else {
None
}
}
fn valid_trace_id(id: &str) -> bool {
id.len() == 32 && is_lower_hex(id) && id != ZERO_TRACE
}
fn valid_span_id(id: &str) -> bool {
id.len() == 16 && is_lower_hex(id) && id != ZERO_SPAN
}
fn is_lower_hex(s: &str) -> bool {
s.bytes()
.all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
}
#[cfg(test)]
mod tests {
use super::*;
const TRACE: &str = "4bf92f3577b34da6a3ce929d0e0e4736";
const SPAN: &str = "00f067aa0ba902b7";
#[test]
fn format_and_parse_round_trip() {
let header = format(TRACE, SPAN).unwrap();
assert_eq!(header, format!("00-{TRACE}-{SPAN}-01"));
assert_eq!(parse(&header), Some((TRACE.to_string(), SPAN.to_string())));
}
#[test]
fn invalid_values_are_rejected() {
assert_eq!(format("short", SPAN), None);
assert_eq!(format(ZERO_TRACE, SPAN), None);
assert_eq!(format(TRACE, ZERO_SPAN), None);
assert_eq!(format(&TRACE.to_uppercase(), SPAN), None);
assert_eq!(parse("garbage"), None);
assert_eq!(parse(&format!("00-{ZERO_TRACE}-{SPAN}-01")), None);
assert!(parse(&format!("01-{TRACE}-{SPAN}-01-extra")).is_some());
}
}