tracing_capture/predicates/
target.rs

1//! `target()` predicate factory.
2
3use predicates::{
4    reflection::{Case, PredicateReflection, Product},
5    Predicate,
6};
7
8use std::fmt;
9
10use crate::Captured;
11
12/// Conversion into a predicate for the target used in the [`target()`] function.
13pub trait IntoTargetPredicate {
14    /// Predicate output of the conversion. The exact type should be considered an implementation
15    /// detail and should not be relied upon.
16    type Predicate: Predicate<str>;
17    /// Performs the conversion.
18    fn into_predicate(self) -> Self::Predicate;
19}
20
21impl<P: Predicate<str>> IntoTargetPredicate for [P; 1] {
22    type Predicate = P;
23
24    fn into_predicate(self) -> Self::Predicate {
25        self.into_iter().next().unwrap()
26    }
27}
28
29impl<'a> IntoTargetPredicate for &'a str {
30    type Predicate = TargetStrPredicate<'a>;
31
32    fn into_predicate(self) -> Self::Predicate {
33        TargetStrPredicate { prefix: self }
34    }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct TargetStrPredicate<'a> {
39    prefix: &'a str,
40}
41
42impl fmt::Display for TargetStrPredicate<'_> {
43    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44        write!(formatter, "target ^= {}", self.prefix)
45    }
46}
47
48impl PredicateReflection for TargetStrPredicate<'_> {}
49
50impl Predicate<str> for TargetStrPredicate<'_> {
51    fn eval(&self, variable: &str) -> bool {
52        variable
53            .strip_prefix(self.prefix)
54            .map_or(false, |stripped| {
55                stripped.is_empty() || stripped.starts_with("::")
56            })
57    }
58
59    fn find_case(&self, expected: bool, variable: &str) -> Option<Case<'_>> {
60        if self.eval(variable) == expected {
61            let product = Product::new("target", variable.to_owned());
62            Some(Case::new(Some(self), expected).add_product(product))
63        } else {
64            None
65        }
66    }
67}
68
69/// Creates a predicate for the target of a [`CapturedSpan`] or [`CapturedEvent`].
70///
71/// # Arguments
72///
73/// The argument of this function may be:
74///
75/// - `&str`: will be compared as per standard target filtering. E.g., `target("tracing")`
76///   will match `tracing` and `tracing::predicate` targets, but not `tracing_capture`.
77/// - Any `str` `Predicate`. To bypass Rust orphaning rules, the predicate
78///   must be enclosed in square brackets (i.e., a one-value array).
79///
80/// [`CapturedSpan`]: crate::CapturedSpan
81/// [`CapturedEvent`]: crate::CapturedEvent
82///
83/// # Examples
84///
85/// ```
86/// # use predicates::str::starts_with;
87/// # use tracing_subscriber::{layer::SubscriberExt, Registry};
88/// # use tracing_capture::{predicates::{target, ScanExt}, CaptureLayer, SharedStorage};
89/// let storage = SharedStorage::default();
90/// let subscriber = Registry::default().with(CaptureLayer::new(&storage));
91/// tracing::subscriber::with_default(subscriber, || {
92///     tracing::info_span!(target: "capture::test", "compute").in_scope(|| {
93///         tracing::info!(answer = 42, "done");
94///     });
95/// });
96///
97/// let storage = storage.lock();
98/// // All of these access the single captured span.
99/// let spans = storage.scan_spans();
100/// let _ = spans.single(&target("capture"));
101/// let _ = spans.single(&target([starts_with("cap")]));
102/// ```
103pub fn target<P: IntoTargetPredicate>(matches: P) -> TargetPredicate<P::Predicate> {
104    TargetPredicate {
105        matches: matches.into_predicate(),
106    }
107}
108
109/// Predicate for the target of a [`CapturedSpan`] or [`CapturedEvent`] returned by
110/// the [`target()`] function.
111///
112/// [`CapturedSpan`]: crate::CapturedSpan
113/// [`CapturedEvent`]: crate::CapturedEvent
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub struct TargetPredicate<P> {
116    matches: P,
117}
118
119impl_bool_ops!(TargetPredicate<P>);
120
121impl<P: Predicate<str>> fmt::Display for TargetPredicate<P> {
122    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(formatter, "target({})", self.matches)
124    }
125}
126
127impl<P: Predicate<str>> PredicateReflection for TargetPredicate<P> {}
128
129impl<'a, P: Predicate<str>, T: Captured<'a>> Predicate<T> for TargetPredicate<P> {
130    fn eval(&self, variable: &T) -> bool {
131        self.matches.eval(variable.metadata().target())
132    }
133
134    fn find_case(&self, expected: bool, variable: &T) -> Option<Case<'_>> {
135        let child = self
136            .matches
137            .find_case(expected, variable.metadata().target())?;
138        Some(Case::new(Some(self), expected).add_child(child))
139    }
140}