tracing_capture/predicates/
ext.rs

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