use opentelemetry::{SpanId, TraceFlags, TraceId};
#[derive(Debug)]
pub(super) struct XRayTraceHeader {
pub(super) trace_id: TraceId,
pub(super) parent_id: SpanId,
pub(super) sampled: TraceFlags,
}
impl XRayTraceHeader {
const ROOT: &str = "Root";
const PARENT: &str = "Parent";
const SAMPLE: &str = "Sampled";
const LINEAGE: &str = "Lineage";
const HEADER_DELIMITER: &str = ";";
}
impl core::str::FromStr for XRayTraceHeader {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut xray_header = Self {
trace_id: TraceId::INVALID,
parent_id: SpanId::INVALID,
sampled: TraceFlags::SAMPLED,
};
let mut trace_id_collected = false;
let mut parent_id_collected = false;
let mut sampled_collected = false;
fn map_err(e: impl ToString) -> String {
e.to_string()
}
for (key, value) in s
.split(Self::HEADER_DELIMITER)
.filter_map(|part| part.split_once('='))
{
match key {
Self::ROOT if !trace_id_collected => {
xray_header.trace_id =
TraceId::from_hex(&value.split('-').skip(1).collect::<String>())
.map_err(map_err)?;
trace_id_collected = true;
}
Self::PARENT if !parent_id_collected => {
xray_header.parent_id = SpanId::from_hex(value).map_err(map_err)?;
parent_id_collected = true;
}
Self::SAMPLE if !sampled_collected => {
xray_header.sampled = match value {
"0" => TraceFlags::NOT_SAMPLED,
"1" => TraceFlags::SAMPLED,
_ => return Err("Invalid Trace header".to_owned()),
};
sampled_collected = true;
}
Self::LINEAGE => {
}
_ => {}
}
}
if !(trace_id_collected && parent_id_collected && sampled_collected) {
return Err("Invalid Trace header".to_owned());
}
Ok(xray_header)
}
}
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive]
pub enum OTelFaasTrigger {
#[default]
Datasource,
Http,
PubSub,
Timer,
Other,
}
impl std::fmt::Display for OTelFaasTrigger {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OTelFaasTrigger::Datasource => write!(f, "datasource"),
OTelFaasTrigger::Http => write!(f, "http"),
OTelFaasTrigger::PubSub => write!(f, "pubsub"),
OTelFaasTrigger::Timer => write!(f, "timer"),
OTelFaasTrigger::Other => write!(f, "other"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::{SpanId, TraceFlags, TraceId};
#[test]
fn xray_trace_header_valid_sampled_1() {
let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1";
let parsed: XRayTraceHeader = header.parse().unwrap();
assert_eq!(
parsed.trace_id,
TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
);
assert_eq!(
parsed.parent_id,
SpanId::from_hex("53995c3f42cd8ad8").unwrap()
);
assert_eq!(parsed.sampled, TraceFlags::SAMPLED);
}
#[test]
fn xray_trace_header_valid_sampled_0() {
let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=0";
let parsed: XRayTraceHeader = header.parse().unwrap();
assert_eq!(
parsed.trace_id,
TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
);
assert_eq!(
parsed.parent_id,
SpanId::from_hex("53995c3f42cd8ad8").unwrap()
);
assert_eq!(parsed.sampled, TraceFlags::NOT_SAMPLED);
}
#[test]
fn xray_trace_header_valid_with_lineage_field() {
let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;Lineage=a87bd80c:1|68fd508a:5|c512fbe3:2";
let parsed: XRayTraceHeader = header.parse().unwrap();
assert_eq!(
parsed.trace_id,
TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
);
assert_eq!(
parsed.parent_id,
SpanId::from_hex("53995c3f42cd8ad8").unwrap()
);
assert_eq!(parsed.sampled, TraceFlags::SAMPLED);
}
#[test]
fn xray_trace_header_valid_with_unknown_fields() {
let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;FutureField=somevalue";
let parsed: XRayTraceHeader = header.parse().unwrap();
assert_eq!(
parsed.trace_id,
TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
);
assert_eq!(
parsed.parent_id,
SpanId::from_hex("53995c3f42cd8ad8").unwrap()
);
assert_eq!(parsed.sampled, TraceFlags::SAMPLED);
}
#[test]
fn xray_trace_header_missing_parent_field() {
let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Sampled=1";
let result: Result<XRayTraceHeader, _> = header.parse();
assert!(result.is_err());
}
#[test]
fn xray_trace_header_invalid_sampled_value() {
let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=2";
let result: Result<XRayTraceHeader, _> = header.parse();
assert!(result.is_err());
}
#[test]
fn xray_trace_header_empty_string() {
let result: Result<XRayTraceHeader, _> = "".parse();
assert!(result.is_err());
}
}