1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! `level()` predicate factory.

use predicates::{
    reflection::{Case, PredicateReflection},
    Predicate,
};
use tracing_core::{Level, LevelFilter};

use std::fmt;

use crate::Captured;

/// Conversion into a predicate for [`Level`]s used in the [`level()`] function.
pub trait IntoLevelPredicate {
    /// Predicate output of the conversion. The exact type should be considered an implementation
    /// detail and should not be relied upon.
    type Predicate: Predicate<Level>;
    /// Performs the conversion.
    fn into_predicate(self) -> Self::Predicate;
}

impl<P: Predicate<Level>> IntoLevelPredicate for [P; 1] {
    type Predicate = P;

    fn into_predicate(self) -> Self::Predicate {
        self.into_iter().next().unwrap()
    }
}

impl IntoLevelPredicate for Level {
    type Predicate = predicates::ord::EqPredicate<Level>;

    fn into_predicate(self) -> Self::Predicate {
        predicates::ord::eq(self)
    }
}

impl IntoLevelPredicate for LevelFilter {
    type Predicate = predicates::ord::OrdPredicate<Level>;

    fn into_predicate(self) -> Self::Predicate {
        self.into_level()
            .map_or_else(|| predicates::ord::lt(Level::ERROR), predicates::ord::le)
    }
}

/// Creates a predicate for the [`Level`] of a [`CapturedSpan`] or [`CapturedEvent`].
///
/// # Arguments
///
/// The argument of this function may be:
///
/// - [`Level`]: will be compared exactly
/// - [`LevelFilter`]: will be compared as per ordinary rules
/// - Any `Predicate` for [`Level`]. To bypass Rust orphaning rules, the predicate
///   must be enclosed in square brackets (i.e., a one-value array).
///
/// [`CapturedSpan`]: crate::CapturedSpan
/// [`CapturedEvent`]: crate::CapturedEvent
///
/// # Examples
///
/// ```
/// # use predicates::ord::gt;
/// # use tracing_core::{Level, LevelFilter};
/// # use tracing_subscriber::{layer::SubscriberExt, Registry};
/// # use tracing_capture::{predicates::{level, ScanExt}, CaptureLayer, SharedStorage};
/// let storage = SharedStorage::default();
/// let subscriber = Registry::default().with(CaptureLayer::new(&storage));
/// tracing::subscriber::with_default(subscriber, || {
///     tracing::info_span!("compute").in_scope(|| {
///         tracing::info!(answer = 42, "done");
///     });
/// });
///
/// let storage = storage.lock();
/// // All of these access the single captured span.
/// let spans = storage.scan_spans();
/// let _ = spans.single(&level(Level::INFO));
/// let _ = spans.first(&level(LevelFilter::DEBUG));
/// let _ = spans.last(&level([gt(Level::WARN)]));
/// ```
pub fn level<P: IntoLevelPredicate>(matches: P) -> LevelPredicate<P::Predicate> {
    LevelPredicate {
        matches: matches.into_predicate(),
    }
}

/// Predicate for the [`Level`] of a [`CapturedSpan`] or [`CapturedEvent`] returned by
/// the [`level()`] function.
///
/// [`CapturedSpan`]: crate::CapturedSpan
/// [`CapturedEvent`]: crate::CapturedEvent
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LevelPredicate<P> {
    matches: P,
}

impl_bool_ops!(LevelPredicate<P>);

impl<P: Predicate<Level>> fmt::Display for LevelPredicate<P> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "level({})", self.matches)
    }
}

impl<P: Predicate<Level>> PredicateReflection for LevelPredicate<P> {}

impl<'a, P: Predicate<Level>, T: Captured<'a>> Predicate<T> for LevelPredicate<P> {
    fn eval(&self, variable: &T) -> bool {
        self.matches.eval(variable.metadata().level())
    }

    fn find_case(&self, expected: bool, variable: &T) -> Option<Case<'_>> {
        let child = self
            .matches
            .find_case(expected, variable.metadata().level())?;
        Some(Case::new(Some(self), expected).add_child(child))
    }
}