dynamecs_analyze/
span_tree.rs1use crate::SpanPath;
2use itertools::izip;
3use std::fmt::{Debug, Display, Formatter};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct SpanTree<Payload> {
7 tree_depth_first: Vec<SpanPath>,
9 payloads: Vec<Payload>,
10 }
13
14#[derive(Debug, Clone)]
15pub struct SpanTreeError {
16 message: String,
17}
18
19impl Display for SpanTreeError {
20 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
21 write!(f, "{}", self.message)
22 }
23}
24
25impl std::error::Error for SpanTreeError {}
26
27impl SpanTreeError {
28 fn message(message: impl Into<String>) -> Self {
29 Self {
30 message: message.into(),
31 }
32 }
33}
34
35impl<Payload> SpanTree<Payload> {
36 pub fn root(&self) -> Option<SpanTreeNode<Payload>> {
37 debug_assert_eq!(self.tree_depth_first.len(), self.payloads.len());
38 (!self.tree_depth_first.is_empty()).then(|| SpanTreeNode {
39 tree_depth_first: &self.tree_depth_first,
40 payloads: &self.payloads,
41 index: 0,
42 })
43 }
44
45 pub fn try_from_depth_first_ordering(paths: Vec<SpanPath>, payloads: Vec<Payload>) -> Result<Self, SpanTreeError> {
46 if let Some((root, others)) = paths.split_first() {
47 let mut stack = Vec::new();
48 for name in root.span_names() {
49 stack.push(name.as_str());
50 }
51
52 for path in others {
53 let num_common_names = izip!(&stack, path.span_names())
54 .take_while(|(&stack_name, path_name)| stack_name == path_name.as_str())
55 .count();
56
57 stack.truncate(num_common_names);
58 if num_common_names < root.depth() {
59 return Err(SpanTreeError::message(
60 "first path is not an ancestor of all other nodes",
61 ));
62 }
63
64 if path.depth() > num_common_names + 1 {
65 return Err(SpanTreeError::message("a non-root node is missing its parent"));
66 } else if path.depth() == num_common_names + 1 {
67 stack.push(path.span_name().unwrap());
68 } else if path.depth() == num_common_names {
69 return Err(SpanTreeError::message("duplicate paths detected"));
70 } else {
71 unreachable!(
72 "by definition, path depth cannot be smaller \
73 than the number of common span names"
74 )
75 }
76 }
77 }
78
79 assert_eq!(paths.len(), payloads.len());
80 Ok(Self {
81 tree_depth_first: paths,
82 payloads,
83 })
84 }
85
86 pub fn transform_payloads<Payload2>(
89 &mut self,
90 transform: impl FnMut(SpanTreeNode<Payload>) -> Payload2,
91 ) -> SpanTree<Payload2> {
92 let new_payloads: Vec<_> = (0..self.tree_depth_first.len())
93 .map(|i| SpanTreeNode {
94 tree_depth_first: &self.tree_depth_first,
95 payloads: &self.payloads,
96 index: i,
97 })
98 .map(transform)
99 .collect();
100
101 SpanTree {
102 tree_depth_first: self.tree_depth_first.clone(),
103 payloads: new_payloads,
104 }
105 }
106}
107
108pub struct SpanTreeNode<'a, Payload> {
109 tree_depth_first: &'a [SpanPath],
110 payloads: &'a [Payload],
111 index: usize,
112}
113
114impl<'a, Payload> Debug for SpanTreeNode<'a, Payload>
115where
116 Payload: Debug,
117{
118 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119 let Self {
120 tree_depth_first,
121 payloads,
122 index,
123 } = self;
124 f.debug_struct("SpanTreeNode")
125 .field("path", &tree_depth_first[*index])
126 .field("payload", &payloads[*index])
127 .finish()
128 }
129}
130
131impl<'a, Payload> SpanTreeNode<'a, Payload> {
132 pub fn payload(&self) -> &Payload {
133 &self.payloads[self.index]
134 }
135
136 pub fn path(&self) -> SpanPath {
137 self.tree_depth_first[self.index].clone()
138 }
139
140 pub fn count_children(&self) -> usize {
141 self.visit_children().count()
142 }
143
144 pub fn root(&self) -> SpanTreeNode<'a, Payload> {
145 SpanTreeNode { index: 0, ..*self }
146 }
147
148 pub fn parent(&self) -> Option<SpanTreeNode<'a, Payload>> {
149 self.path().parent().and_then(|parent_path| {
150 self.tree_depth_first[..self.index]
151 .binary_search_by_key(&parent_path.span_names(), |path| path.span_names())
152 .ok()
153 .map(|index| SpanTreeNode {
154 tree_depth_first: self.tree_depth_first,
155 payloads: self.payloads,
156 index,
157 })
158 })
159 }
160
161 pub fn visit_children(&self) -> impl Iterator<Item = SpanTreeNode<'a, Payload>> {
162 let tree_depth_first: &'a [SpanPath] = self.tree_depth_first;
165 let payloads: &'a [Payload] = self.payloads;
166
167 let self_path1 = self.path();
171 let self_path2 = self_path1.clone();
172
173 self.tree_depth_first
177 .iter()
178 .enumerate()
179 .skip(self.index + 1)
181 .take_while(move |(_, maybe_child)| self_path1.is_ancestor_of(maybe_child))
182 .filter(move |(_, descendant)| self_path2.is_parent_of(descendant))
183 .map(move |(child_index, _)| SpanTreeNode {
184 tree_depth_first,
185 payloads,
186 index: child_index,
187 })
188 }
189}