use std::{error::Error, fmt};
use saddle_core::TraceId;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum InboundTrace {
Inherited,
Created,
ReplacedInvalid,
}
impl InboundTrace {
pub(crate) const fn as_str(self) -> &'static str {
match self {
Self::Inherited => "inherited",
Self::Created => "created",
Self::ReplacedInvalid => "replaced_invalid",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TraceIdError {
InvalidLength,
InvalidHex,
Zero,
}
impl fmt::Display for TraceIdError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::InvalidLength => "trace id must contain exactly 32 hexadecimal characters",
Self::InvalidHex => "trace id contains a non-hexadecimal character",
Self::Zero => "trace id must not be zero",
})
}
}
impl Error for TraceIdError {}
pub(crate) fn select_trace_id(
value: Option<&str>,
generated: impl FnOnce() -> TraceId,
) -> (TraceId, InboundTrace) {
match value {
Some(value) => match trace_id_from_hex(value) {
Ok(trace_id) => (trace_id, InboundTrace::Inherited),
Err(_) => (generated(), InboundTrace::ReplacedInvalid),
},
None => (generated(), InboundTrace::Created),
}
}
pub fn trace_id_from_hex(value: &str) -> Result<TraceId, TraceIdError> {
if value.len() != 32 {
return Err(TraceIdError::InvalidLength);
}
if !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
return Err(TraceIdError::InvalidHex);
}
let value = u128::from_str_radix(value, 16).map_err(|_| TraceIdError::InvalidHex)?;
if value == 0 {
return Err(TraceIdError::Zero);
}
Ok(TraceId::from_u128(value))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn accepts_only_non_zero_fixed_width_hex_trace_ids() {
let expected = "00112233445566778899aabbccddeeff";
assert_eq!(trace_id_from_hex(expected).unwrap().to_string(), expected);
assert_eq!(trace_id_from_hex("abcd"), Err(TraceIdError::InvalidLength));
assert_eq!(
trace_id_from_hex("00112233445566778899aabbccddeefg"),
Err(TraceIdError::InvalidHex)
);
assert_eq!(
trace_id_from_hex("00000000000000000000000000000000"),
Err(TraceIdError::Zero)
);
}
}