tracing_capture/predicates/
target.rs

1//! `target()` predicate factory.
2
3use std::fmt;
4
5use predicates::{
6    reflection::{Case, PredicateReflection, Product},
7    Predicate,
8};
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            .is_some_and(|stripped| stripped.is_empty() || stripped.starts_with("::"))
55    }
56
57    fn find_case(&self, expected: bool, variable: &str) -> Option<Case<'_>> {
58        if self.eval(variable) == expected {
59            let product = Product::new("target", variable.to_owned());
60            Some(Case::new(Some(self), expected).add_product(product))
61        } else {
62            None
63        }
64    }
65}
66
67/// Creates a predicate for the target of a [`CapturedSpan`] or [`CapturedEvent`].
68///
69/// # Arguments
70///
71/// The argument of this function may be:
72///
73/// - `&str`: will be compared as per standard target filtering. E.g., `target("tracing")`
74///   will match `tracing` and `tracing::predicate` targets, but not `tracing_capture`.
75/// - Any `str` `Predicate`. To bypass Rust orphaning rules, the predicate
76///   must be enclosed in square brackets (i.e., a one-value array).
77///
78/// [`CapturedSpan`]: crate::CapturedSpan
79/// [`CapturedEvent`]: crate::CapturedEvent
80///
81/// # Examples
82///
83/// ```
84/// # use predicates::str::starts_with;
85/// # use tracing_subscriber::{layer::SubscriberExt, Registry};
86/// # use tracing_capture::{predicates::{target, ScanExt}, CaptureLayer, SharedStorage};
87/// let storage = SharedStorage::default();
88/// let subscriber = Registry::default().with(CaptureLayer::new(&storage));
89/// tracing::subscriber::with_default(subscriber, || {
90///     tracing::info_span!(target: "capture::test", "compute").in_scope(|| {
91///         tracing::info!(answer = 42, "done");
92///     });
93/// });
94///
95/// let storage = storage.lock();
96/// // All of these access the single captured span.
97/// let spans = storage.scan_spans();
98/// let _ = spans.single(&target("capture"));
99/// let _ = spans.single(&target([starts_with("cap")]));
100/// ```
101pub fn target<P: IntoTargetPredicate>(matches: P) -> TargetPredicate<P::Predicate> {
102    TargetPredicate {
103        matches: matches.into_predicate(),
104    }
105}
106
107/// Predicate for the target of a [`CapturedSpan`] or [`CapturedEvent`] returned by
108/// the [`target()`] function.
109///
110/// [`CapturedSpan`]: crate::CapturedSpan
111/// [`CapturedEvent`]: crate::CapturedEvent
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub struct TargetPredicate<P> {
114    matches: P,
115}
116
117impl_bool_ops!(TargetPredicate<P>);
118
119impl<P: Predicate<str>> fmt::Display for TargetPredicate<P> {
120    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(formatter, "target({})", self.matches)
122    }
123}
124
125impl<P: Predicate<str>> PredicateReflection for TargetPredicate<P> {}
126
127impl<'a, P: Predicate<str>, T: Captured<'a>> Predicate<T> for TargetPredicate<P> {
128    fn eval(&self, variable: &T) -> bool {
129        self.matches.eval(variable.metadata().target())
130    }
131
132    fn find_case(&self, expected: bool, variable: &T) -> Option<Case<'_>> {
133        let child = self
134            .matches
135            .find_case(expected, variable.metadata().target())?;
136        Some(Case::new(Some(self), expected).add_child(child))
137    }
138}