use axum::http::HeaderMap;
use fraiseql_core::federation::FederationTraceContext;
pub fn extract_trace_context(headers: &HeaderMap) -> Option<FederationTraceContext> {
headers
.get("traceparent")
.and_then(|h| h.to_str().ok())
.and_then(FederationTraceContext::from_traceparent)
}
#[must_use]
pub fn extract_trace_id(headers: &HeaderMap) -> Option<String> {
let value = headers.get("traceparent")?.to_str().ok()?;
let trace_id = value.split('-').nth(1)?;
let valid = trace_id.len() == 32
&& trace_id.bytes().all(|b| b.is_ascii_hexdigit())
&& trace_id.bytes().any(|b| b != b'0');
valid.then(|| trace_id.to_ascii_lowercase())
}
#[must_use]
pub fn extract_trace_context_json(headers: &HeaderMap) -> Option<serde_json::Value> {
let traceparent = headers.get("traceparent")?.to_str().ok()?;
let mut parts = traceparent.split('-');
let (version, trace_id, parent_id, trace_flags) =
(parts.next()?, parts.next()?, parts.next()?, parts.next()?);
let is_hex = |s: &str, len: usize| s.len() == len && s.bytes().all(|b| b.is_ascii_hexdigit());
let valid = is_hex(version, 2)
&& is_hex(trace_id, 32)
&& trace_id.bytes().any(|b| b != b'0')
&& is_hex(parent_id, 16)
&& is_hex(trace_flags, 2);
if !valid {
return None;
}
let mut obj = serde_json::Map::with_capacity(5);
obj.insert("version".to_owned(), version.to_ascii_lowercase().into());
obj.insert("trace_id".to_owned(), trace_id.to_ascii_lowercase().into());
obj.insert("parent_id".to_owned(), parent_id.to_ascii_lowercase().into());
obj.insert("trace_flags".to_owned(), trace_flags.to_ascii_lowercase().into());
if let Some(tracestate) = headers
.get("tracestate")
.and_then(|h| h.to_str().ok())
.map(str::trim)
.filter(|s| !s.is_empty())
{
obj.insert("tracestate".to_owned(), tracestate.into());
}
Some(serde_json::Value::Object(obj))
}