tracing_capture/predicates/
ext.rs

1//! Extension trait for asserting against collections of `CapturedEvent`s and `CapturedSpan`s.
2
3use std::fmt;
4
5use predicates::Predicate;
6
7use crate::{CapturedEvent, CapturedEvents, CapturedSpan, CapturedSpans, DescendantSpans, Storage};
8
9/// Helper to wrap holders of [`CapturedSpan`]s or [`CapturedEvent`]s
10/// (spans or the underlying [`Storage`]) so that they are more convenient to use with `Predicate`s.
11///
12/// See [the module-level docs](crate::predicates) for examples of usage.
13///
14/// [`CapturedEvent`]: crate::CapturedEvent
15pub trait ScanExt<'a>: Sized {
16    /// Creates a scanner for the spans contained by this holder. What is meant by "contained"
17    /// (i.e., whether the scan is deep or shallow), depends on the holder type and is documented
18    /// at the corresponding impl.
19    fn scan_spans(self) -> Scanner<Self, CapturedSpans<'a>>;
20    /// Creates a scanner for the events contained by this holder. What is meant by "contained"
21    /// (i.e., whether the scan is deep or shallow), depends on the holder type and is documented
22    /// at the corresponding impl.
23    fn scan_events(self) -> Scanner<Self, CapturedEvents<'a>>;
24}
25
26/// Scans for `Storage` are deep; they include *all* captured spans / events, not just root ones.
27impl<'a> ScanExt<'a> for &'a Storage {
28    fn scan_spans(self) -> Scanner<Self, CapturedSpans<'a>> {
29        Scanner::new(self, Storage::all_spans)
30    }
31
32    fn scan_events(self) -> Scanner<Self, CapturedEvents<'a>> {
33        Scanner::new(self, Storage::all_events)
34    }
35}
36
37/// Scans for `CapturedSpan` are shallow, i.e. include only direct children spans / events.
38impl<'a> ScanExt<'a> for CapturedSpan<'a> {
39    fn scan_spans(self) -> Scanner<Self, CapturedSpans<'a>> {
40        Scanner::new(self, |span| span.children())
41    }
42
43    fn scan_events(self) -> Scanner<Self, CapturedEvents<'a>> {
44        Scanner::new(self, |span| span.events())
45    }
46}
47
48impl<'a> CapturedSpan<'a> {
49    /// Deeply scans all descendants of this span.
50    pub fn deep_scan_spans(self) -> Scanner<Self, DescendantSpans<'a>> {
51        Scanner::new(self, |span| span.descendants())
52    }
53
54    /// Deeply scans all descendant events of this span.
55    pub fn deep_scan_events(self) -> Scanner<Self, impl Iterator<Item = CapturedEvent<'a>> + 'a> {
56        Scanner::new(self, |span| span.events().chain(span.descendant_events()))
57    }
58}
59
60/// Helper that allows using `Predicate`s rather than closures to find matching elements,
61/// and provides more informative error messages.
62///
63/// Returned by the [`ScanExt`] methods; see its docs for more details.
64#[derive(Debug)]
65pub struct Scanner<T, I> {
66    items: T,
67    into_iter: fn(T) -> I,
68}
69
70impl<T: Clone, I> Clone for Scanner<T, I> {
71    fn clone(&self) -> Self {
72        Self {
73            items: self.items.clone(),
74            into_iter: self.into_iter,
75        }
76    }
77}
78
79impl<T: Copy, I> Copy for Scanner<T, I> {}
80
81impl<T, I> Scanner<T, I>
82where
83    I: Iterator,
84    I::Item: fmt::Debug,
85{
86    fn new(items: T, into_iter: fn(T) -> I) -> Self {
87        Self { items, into_iter }
88    }
89
90    fn iter(self) -> I {
91        (self.into_iter)(self.items)
92    }
93
94    /// Finds the single item matching the predicate.
95    ///
96    /// # Panics
97    ///
98    /// Panics with an informative message if no items, or multiple items match the predicate.
99    pub fn single<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) -> I::Item {
100        let mut iter = self.iter();
101        let first = iter
102            .find(|item| predicate.eval(item))
103            .unwrap_or_else(|| panic!("no items have matched predicate {predicate}"));
104
105        let second = iter.find(|item| predicate.eval(item));
106        if let Some(second) = second {
107            panic!(
108                "multiple items match predicate {predicate}: {:#?}",
109                [first, second]
110            );
111        }
112        first
113    }
114
115    /// Finds the first item matching the predicate.
116    ///
117    /// # Panics
118    ///
119    /// Panics with an informative message if no items match the predicate.
120    pub fn first<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) -> I::Item {
121        let mut iter = self.iter();
122        iter.find(|item| predicate.eval(item))
123            .unwrap_or_else(|| panic!("no items have matched predicate {predicate}"))
124    }
125
126    /// Checks that all of the items match the predicate.
127    ///
128    /// # Panics
129    ///
130    /// Panics with an informative message if any of items does not match the predicate.
131    pub fn all<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) {
132        let mut iter = self.iter();
133        if let Some(item) = iter.find(|item| !predicate.eval(item)) {
134            panic!("item does not match predicate {predicate}: {item:#?}");
135        }
136    }
137
138    /// Checks that none of the items match the predicate.
139    ///
140    /// # Panics
141    ///
142    /// Panics with an informative message if any of items match the predicate.
143    pub fn none<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) {
144        let mut iter = self.iter();
145        if let Some(item) = iter.find(|item| predicate.eval(item)) {
146            panic!("item matched predicate {predicate}: {item:#?}");
147        }
148    }
149}
150
151impl<T, I> Scanner<T, I>
152where
153    I: DoubleEndedIterator,
154    I::Item: fmt::Debug,
155{
156    /// Finds the last item matching the predicate.
157    ///
158    /// # Panics
159    ///
160    /// Panics with an informative message if no items match the predicate.
161    pub fn last<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) -> I::Item {
162        let mut iter = self.iter().rev();
163        iter.find(|item| predicate.eval(item))
164            .unwrap_or_else(|| panic!("no items have matched predicate {predicate}"))
165    }
166}