1//! Extension trait for asserting against collections of `CapturedEvent`s and `CapturedSpan`s.
23use predicates::Predicate;
45use std::fmt;
67use crate::{
8 CapturedEvents, CapturedSpan, CapturedSpans, DescendantEvents, DescendantSpans, Storage,
9};
1011/// 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.
21fn 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.
25fn scan_events(self) -> Scanner<Self, CapturedEvents<'a>>;
26}
2728/// Scans for `Storage` are deep; they include *all* captured spans / events, not just root ones.
29impl<'a> ScanExt<'a> for &'a Storage {
30fn scan_spans(self) -> Scanner<Self, CapturedSpans<'a>> {
31 Scanner::new(self, Storage::all_spans)
32 }
3334fn scan_events(self) -> Scanner<Self, CapturedEvents<'a>> {
35 Scanner::new(self, Storage::all_events)
36 }
37}
3839/// Scans for `CapturedSpan` are shallow, i.e. include only direct children spans / events.
40impl<'a> ScanExt<'a> for CapturedSpan<'a> {
41fn scan_spans(self) -> Scanner<Self, CapturedSpans<'a>> {
42 Scanner::new(self, |span| span.children())
43 }
4445fn scan_events(self) -> Scanner<Self, CapturedEvents<'a>> {
46 Scanner::new(self, |span| span.events())
47 }
48}
4950impl<'a> CapturedSpan<'a> {
51/// Deeply scans all descendants of this span.
52pub fn deep_scan_spans(self) -> Scanner<Self, DescendantSpans<'a>> {
53 Scanner::new(self, |span| span.descendants())
54 }
5556/// Deeply scans all descendant events of this span.
57pub fn deep_scan_events(self) -> Scanner<Self, DescendantEvents<'a>> {
58 Scanner::new(self, |span| span.descendant_events())
59 }
60}
6162/// 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}
7172impl<T: Clone, I> Clone for Scanner<T, I> {
73fn clone(&self) -> Self {
74Self {
75 items: self.items.clone(),
76 into_iter: self.into_iter,
77 }
78 }
79}
8081impl<T: Copy, I> Copy for Scanner<T, I> {}
8283impl<T, I> Scanner<T, I>
84where
85I: Iterator,
86 I::Item: fmt::Debug,
87{
88fn new(items: T, into_iter: fn(T) -> I) -> Self {
89Self { items, into_iter }
90 }
9192fn iter(self) -> I {
93 (self.into_iter)(self.items)
94 }
9596/// 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.
101pub fn single<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) -> I::Item {
102let mut iter = self.iter();
103let first = iter
104 .find(|item| predicate.eval(item))
105 .unwrap_or_else(|| panic!("no items have matched predicate {predicate}"));
106107let second = iter.find(|item| predicate.eval(item));
108if let Some(second) = second {
109panic!(
110"multiple items match predicate {predicate}: {:#?}",
111 [first, second]
112 );
113 }
114 first
115 }
116117/// Finds the first item matching the predicate.
118 ///
119 /// # Panics
120 ///
121 /// Panics with an informative message if no items match the predicate.
122pub fn first<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) -> I::Item {
123let mut iter = self.iter();
124 iter.find(|item| predicate.eval(item))
125 .unwrap_or_else(|| panic!("no items have matched predicate {predicate}"))
126 }
127128/// 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.
133pub fn all<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) {
134let mut iter = self.iter();
135if let Some(item) = iter.find(|item| !predicate.eval(item)) {
136panic!("item does not match predicate {predicate}: {item:#?}");
137 }
138 }
139140/// 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.
145pub fn none<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) {
146let mut iter = self.iter();
147if let Some(item) = iter.find(|item| predicate.eval(item)) {
148panic!("item matched predicate {predicate}: {item:#?}");
149 }
150 }
151}
152153impl<T, I> Scanner<T, I>
154where
155I: 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.
163pub fn last<P: Predicate<I::Item> + ?Sized>(self, predicate: &P) -> I::Item {
164let 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}