use multimap::MultiMap;
use opentelemetry::trace::{SpanId, TraceId};
use opentelemetry_sdk::export::trace::SpanData;
use std::fmt::{Display, Formatter};
use treeline::Tree;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TestSpan {
id: TestSpanId,
parent_span_id: SpanId,
name: String,
attributes: Vec<(String, String)>,
}
impl Display for TestSpan {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name.to_string().as_str())
}
}
impl TestSpan {
pub fn id(&self) -> TestSpanId {
self.id.clone()
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn parent_id(&self) -> TestSpanId {
TestSpanId {
trace_id: self.trace_id(),
span_id: self.parent_span_id,
}
}
pub fn trace_id(&self) -> TraceId {
self.id.trace_id
}
pub fn from_span_data(span_data: &SpanData) -> TestSpan {
TestSpan {
id: TestSpanId {
trace_id: span_data.span_context.trace_id(),
span_id: span_data.span_context.span_id(),
},
parent_span_id: span_data.parent_span_id,
name: span_data.name.to_string(),
attributes: span_data
.attributes
.iter()
.map(|kv| (kv.key.to_string(), kv.value.to_string()))
.collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TestSpanId {
trace_id: TraceId,
span_id: SpanId,
}
impl Display for TestSpanId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("{} - {}", self.trace_id, self.span_id))
}
}
#[derive(Debug)]
pub struct Trace(pub Tree<TestSpan>);
impl Display for Trace {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl Trace {
pub fn from_span_data(spans: Vec<SpanData>) -> Vec<Trace> {
let mut spans_by_parent_span_id = MultiMap::new();
for span in &spans {
let test_span = TestSpan::from_span_data(span);
spans_by_parent_span_id.insert(test_span.parent_id(), test_span)
}
let mut test_spans: Vec<TestSpan> = vec![];
for (_, ts) in spans_by_parent_span_id.iter() {
test_spans.push(ts.clone());
}
let roots = test_spans
.iter()
.filter(|ts| ts.parent_span_id == SpanId::from(0));
let mut result = vec![];
for root in roots {
result.push(Trace(Self::make_trace_tree(
root.clone(),
&spans_by_parent_span_id,
)))
}
result
}
fn make_trace_tree(
root: TestSpan,
tree_map: &MultiMap<TestSpanId, TestSpan>,
) -> Tree<TestSpan> {
let mut tree = Tree::root(root.clone());
if let Some(children) = tree_map.get_vec(&root.id()) {
for child in children.iter() {
tree.push(Self::make_trace_tree(child.clone(), tree_map));
}
}
tree
}
}