logged_stream/
filter.rs

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