emit_core/
filter.rs

1/*!
2The [`Filter`] type.
3
4Filters reduce the burden of diagnostics by limiting the volume of data generated. A typical filter will only match events with a certain level or higher, or it may exclude all events for a particularly noisy module.
5*/
6
7use crate::{
8    and::And,
9    empty::Empty,
10    event::{Event, ToEvent},
11    or::Or,
12    props::ErasedProps,
13};
14
15/**
16A filter over [`Event`]s.
17
18Filters can be evaluated with a call to [`Filter::matches`].
19*/
20pub trait Filter {
21    /**
22    Evaluate an event against the filter.
23
24    If this method return `true` then the event has passed the filter. If this method returns `false` then the event has failed the filter.
25    */
26    fn matches<E: ToEvent>(&self, evt: E) -> bool;
27
28    /**
29    `self && other`.
30
31    If `self` evaluates to `true` then `other` will be evaluated.
32    */
33    fn and_when<U>(self, other: U) -> And<Self, U>
34    where
35        Self: Sized,
36    {
37        And::new(self, other)
38    }
39
40    /**
41    `self || other`.
42
43    If `self` evaluates to `false` then `other` will be evaluated.
44    */
45    fn or_when<U>(self, other: U) -> Or<Self, U>
46    where
47        Self: Sized,
48    {
49        Or::new(self, other)
50    }
51}
52
53impl<'a, F: Filter + ?Sized> Filter for &'a F {
54    fn matches<E: ToEvent>(&self, evt: E) -> bool {
55        (**self).matches(evt)
56    }
57}
58
59#[cfg(feature = "alloc")]
60impl<'a, F: Filter + ?Sized + 'a> Filter for alloc::boxed::Box<F> {
61    fn matches<E: ToEvent>(&self, evt: E) -> bool {
62        (**self).matches(evt)
63    }
64}
65
66#[cfg(feature = "alloc")]
67impl<'a, F: Filter + ?Sized + 'a> Filter for alloc::sync::Arc<F> {
68    fn matches<E: ToEvent>(&self, evt: E) -> bool {
69        (**self).matches(evt)
70    }
71}
72
73impl<F: Filter> Filter for Option<F> {
74    fn matches<E: ToEvent>(&self, evt: E) -> bool {
75        match self {
76            Some(filter) => filter.matches(evt),
77            None => Empty.matches(evt),
78        }
79    }
80}
81
82impl Filter for Empty {
83    fn matches<E: ToEvent>(&self, _: E) -> bool {
84        true
85    }
86}
87
88impl Filter for fn(Event<&dyn ErasedProps>) -> bool {
89    fn matches<E: ToEvent>(&self, evt: E) -> bool {
90        (self)(evt.to_event().erase())
91    }
92}
93
94/**
95A [`Filter`] from a function.
96
97This type can be created directly, or via [`from_fn`].
98*/
99pub struct FromFn<F = fn(Event<&dyn ErasedProps>) -> bool>(F);
100
101impl<F> FromFn<F> {
102    /**
103    Wrap the given filter function.
104    */
105    pub const fn new(filter: F) -> FromFn<F> {
106        FromFn(filter)
107    }
108}
109
110impl<F: Fn(Event<&dyn ErasedProps>) -> bool> Filter for FromFn<F> {
111    fn matches<E: ToEvent>(&self, evt: E) -> bool {
112        (self.0)(evt.to_event().erase())
113    }
114}
115
116/**
117Create a [`Filter`] from a function.
118*/
119pub const fn from_fn<F: Fn(Event<&dyn ErasedProps>) -> bool>(f: F) -> FromFn<F> {
120    FromFn(f)
121}
122
123/**
124A [`Filter`] that always matches any event.
125*/
126pub struct Always {}
127
128impl Always {
129    /**
130    Create a filter that matches any event.
131    */
132    pub const fn new() -> Always {
133        Always {}
134    }
135}
136
137impl Filter for Always {
138    fn matches<E: ToEvent>(&self, _: E) -> bool {
139        true
140    }
141}
142
143/**
144Create a [`Filter`] that matches any event.
145*/
146pub const fn always() -> Always {
147    Always::new()
148}
149
150impl<T: Filter, U: Filter> Filter for And<T, U> {
151    fn matches<E: ToEvent>(&self, evt: E) -> bool {
152        let evt = evt.to_event();
153
154        self.left().matches(&evt) && self.right().matches(&evt)
155    }
156}
157
158impl<T: Filter, U: Filter> Filter for Or<T, U> {
159    fn matches<E: ToEvent>(&self, evt: E) -> bool {
160        let evt = evt.to_event();
161
162        self.left().matches(&evt) || self.right().matches(&evt)
163    }
164}
165
166mod internal {
167    use crate::{event::Event, props::ErasedProps};
168
169    pub trait DispatchFilter {
170        fn dispatch_matches(&self, evt: &Event<&dyn ErasedProps>) -> bool;
171    }
172
173    pub trait SealedFilter {
174        fn erase_filter(&self) -> crate::internal::Erased<&dyn DispatchFilter>;
175    }
176}
177
178/**
179An object-safe [`Filter`].
180
181A `dyn ErasedFilter` can be treated as `impl Filter`.
182*/
183pub trait ErasedFilter: internal::SealedFilter {}
184
185impl<T: Filter> ErasedFilter for T {}
186
187impl<T: Filter> internal::SealedFilter for T {
188    fn erase_filter(&self) -> crate::internal::Erased<&dyn internal::DispatchFilter> {
189        crate::internal::Erased(self)
190    }
191}
192
193impl<T: Filter> internal::DispatchFilter for T {
194    fn dispatch_matches(&self, evt: &Event<&dyn ErasedProps>) -> bool {
195        self.matches(evt)
196    }
197}
198
199impl<'a> Filter for dyn ErasedFilter + 'a {
200    fn matches<E: ToEvent>(&self, evt: E) -> bool {
201        self.erase_filter()
202            .0
203            .dispatch_matches(&evt.to_event().erase())
204    }
205}
206
207impl<'a> Filter for dyn ErasedFilter + Send + Sync + 'a {
208    fn matches<E: ToEvent>(&self, evt: E) -> bool {
209        (self as &(dyn ErasedFilter + 'a)).matches(evt)
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use crate::{path::Path, template::Template};
216
217    use super::*;
218
219    struct MyFilter {
220        matches: bool,
221    }
222
223    impl Filter for MyFilter {
224        fn matches<E: ToEvent>(&self, _: E) -> bool {
225            self.matches
226        }
227    }
228
229    #[test]
230    fn option_filter() {
231        for (case, matches) in [(Some(MyFilter { matches: false }), false), (None, true)] {
232            assert_eq!(
233                matches,
234                case.matches(Event::new(
235                    Path::new_raw("module"),
236                    Template::literal("Event"),
237                    Empty,
238                    Empty,
239                ))
240            );
241        }
242    }
243
244    #[test]
245    fn and_filter() {
246        for a in [true, false] {
247            for b in [true, false] {
248                let fa = MyFilter { matches: a };
249                let fb = MyFilter { matches: b };
250
251                assert_eq!(
252                    a && b,
253                    fa.and_when(fb).matches(Event::new(
254                        Path::new_raw("module"),
255                        Template::literal("Event"),
256                        Empty,
257                        Empty,
258                    ))
259                );
260            }
261        }
262    }
263
264    #[test]
265    fn or_filter() {
266        for a in [true, false] {
267            for b in [true, false] {
268                let fa = MyFilter { matches: a };
269                let fb = MyFilter { matches: b };
270
271                assert_eq!(
272                    a || b,
273                    fa.or_when(fb).matches(Event::new(
274                        Path::new_raw("module"),
275                        Template::literal("Event"),
276                        Empty,
277                        Empty,
278                    ))
279                );
280            }
281        }
282    }
283
284    #[test]
285    fn from_fn_filter() {
286        let f = from_fn(|evt| evt.mdl() == Path::new_raw("module"));
287
288        assert!(f.matches(Event::new(
289            Path::new_raw("module"),
290            Template::literal("Event"),
291            Empty,
292            Empty,
293        )));
294
295        assert!(!f.matches(Event::new(
296            Path::new_raw("not_module"),
297            Template::literal("Event"),
298            Empty,
299            Empty,
300        )));
301    }
302
303    #[test]
304    fn always_filter() {
305        let f = always();
306
307        assert!(f.matches(Event::new(
308            Path::new_raw("module"),
309            Template::literal("Event"),
310            Empty,
311            Empty,
312        )));
313    }
314
315    #[test]
316    fn erased_filter() {
317        let f = MyFilter { matches: true };
318        let f = &f as &dyn ErasedFilter;
319
320        assert!(f.matches(Event::new(
321            Path::new_raw("module"),
322            Template::literal("Event"),
323            Empty,
324            Empty,
325        )));
326
327        let f = MyFilter { matches: false };
328        let f = &f as &dyn ErasedFilter;
329
330        assert!(!f.matches(Event::new(
331            Path::new_raw("module"),
332            Template::literal("Event"),
333            Empty,
334            Empty,
335        )));
336    }
337}