logged_stream/
filter.rs

1use crate::Record;
2use crate::RecordKind;
3use itertools::Itertools;
4
5//////////////////////////////////////////////////////////////////////////////////////////////////////////////
6// Trait
7//////////////////////////////////////////////////////////////////////////////////////////////////////////////
8
9/// This trait allows to filter log records ([`Record`]) using [`check`] method which returns [`bool`] value.
10/// It should be implemented for structures which are going to be used as filtering part inside [`LoggedStream`].
11///
12/// [`check`]: RecordFilter::check
13/// [`LoggedStream`]: crate::LoggedStream
14pub trait RecordFilter: Send + 'static {
15    /// This method returns [`bool`] value depending on if received log record ([`Record`]) should be processed
16    /// by logging part inside [`LoggedStream`].
17    ///
18    /// [`LoggedStream`]: crate::LoggedStream
19    fn check(&self, record: &Record) -> bool;
20}
21
22impl RecordFilter for Box<dyn RecordFilter> {
23    fn check(&self, record: &Record) -> bool {
24        (**self).check(record)
25    }
26}
27
28//////////////////////////////////////////////////////////////////////////////////////////////////////////////
29// DefaultFilter
30//////////////////////////////////////////////////////////////////////////////////////////////////////////////
31
32/// This is default implementation of [`RecordFilter`] trait which [`check`] method always return `true`.
33/// It should be constructed using [`Default::default`] method.
34///
35/// [`check`]: RecordFilter::check
36#[derive(Debug, Clone, Copy, Default)]
37pub struct DefaultFilter;
38
39impl RecordFilter for DefaultFilter {
40    #[inline]
41    fn check(&self, _record: &Record) -> bool {
42        true
43    }
44}
45
46impl RecordFilter for Box<DefaultFilter> {
47    fn check(&self, record: &Record) -> bool {
48        (**self).check(record)
49    }
50}
51
52//////////////////////////////////////////////////////////////////////////////////////////////////////////////
53// RecordKindFilter
54//////////////////////////////////////////////////////////////////////////////////////////////////////////////
55
56/// This implementation of [`RecordFilter`] trait accepts allowed log record kinds ([`RecordKind`]) array during
57/// construction and its [`check`] method returns `true` if received log record kind presented inside this array.
58///
59/// [`check`]: RecordFilter::check
60#[derive(Debug)]
61pub struct RecordKindFilter {
62    allowed_kinds: Vec<RecordKind>,
63}
64
65impl RecordKindFilter {
66    /// Construct a new instance of [`RecordKindFilter`] using provided array of allowed log record kinds ([`RecordKind`]).
67    pub fn new(kinds: &'static [RecordKind]) -> Self {
68        Self {
69            allowed_kinds: kinds.iter().copied().unique().collect(),
70        }
71    }
72}
73
74impl RecordFilter for RecordKindFilter {
75    #[inline]
76    fn check(&self, record: &Record) -> bool {
77        self.allowed_kinds.contains(&record.kind)
78    }
79}
80
81impl RecordFilter for Box<RecordKindFilter> {
82    fn check(&self, record: &Record) -> bool {
83        (**self).check(record)
84    }
85}
86
87//////////////////////////////////////////////////////////////////////////////////////////////////////////////
88// Tests
89//////////////////////////////////////////////////////////////////////////////////////////////////////////////
90
91#[cfg(test)]
92mod tests {
93    use crate::filter::DefaultFilter;
94    use crate::filter::RecordFilter;
95    use crate::filter::RecordKindFilter;
96    use crate::record::Record;
97    use crate::record::RecordKind;
98
99    fn assert_unpin<T: Unpin>() {}
100
101    #[test]
102    fn test_unpin() {
103        assert_unpin::<DefaultFilter>();
104        assert_unpin::<RecordKindFilter>();
105    }
106
107    #[test]
108    fn test_default_filter() {
109        assert!(DefaultFilter.check(&Record::new(
110            RecordKind::Read,
111            String::from("01:02:03:04:05:06")
112        )));
113        assert!(DefaultFilter.check(&Record::new(
114            RecordKind::Write,
115            String::from("01:02:03:04:05:06")
116        )));
117        assert!(DefaultFilter.check(&Record::new(RecordKind::Drop, String::from("deallocated"))));
118        assert!(DefaultFilter.check(&Record::new(
119            RecordKind::Shutdown,
120            String::from("write shutdown request")
121        )));
122    }
123
124    #[test]
125    fn test_record_kind_filter() {
126        let filter = RecordKindFilter::new(&[RecordKind::Read]);
127        assert!(filter.check(&Record::new(
128            RecordKind::Read,
129            String::from("01:02:03:04:05:06")
130        )));
131        assert!(!filter.check(&Record::new(
132            RecordKind::Write,
133            String::from("01:02:03:04:05:06")
134        )));
135        assert!(!filter.check(&Record::new(RecordKind::Drop, String::from("deallocated"))));
136        assert!(!filter.check(&Record::new(
137            RecordKind::Shutdown,
138            String::from("write shutdown request")
139        )));
140    }
141
142    #[test]
143    fn test_trait_object_safety() {
144        // Assert traint object construct.
145        let default: Box<dyn RecordFilter> = Box::<DefaultFilter>::default();
146        let record_kind: Box<dyn RecordFilter> = Box::new(RecordKindFilter::new(&[]));
147
148        let record = Record::new(RecordKind::Open, String::from("test log record"));
149
150        // Assert that trait object methods are dispatchable.
151        _ = default.check(&record);
152        _ = record_kind.check(&record);
153    }
154
155    fn assert_record_filter<T: RecordFilter>() {}
156
157    #[test]
158    fn test_box() {
159        assert_record_filter::<Box<dyn RecordFilter>>();
160        assert_record_filter::<Box<RecordKindFilter>>();
161        assert_record_filter::<Box<DefaultFilter>>();
162    }
163
164    fn assert_send<T: Send>() {}
165
166    #[test]
167    fn test_send() {
168        assert_send::<RecordKindFilter>();
169        assert_send::<DefaultFilter>();
170
171        assert_send::<Box<dyn RecordFilter>>();
172        assert_send::<Box<RecordKindFilter>>();
173        assert_send::<Box<DefaultFilter>>();
174    }
175}