nonblock_logger/
filter.rs

1use crate::Error;
2use log::{Level, LevelFilter, Metadata, Record};
3use std::cmp::{max, Ordering};
4
5pub trait Filter: Send + Sync + 'static {
6    fn boxed(self) -> Result<Box<dyn Filter>, Error>;
7    fn log(&self, record: &Record) -> bool;
8    fn enabled(&self, metadata: &Metadata) -> bool;
9    fn maxlevel(&self) -> LevelFilter;
10}
11
12impl Filter for BaseFilter {
13    fn boxed(self) -> Result<Box<dyn Filter>, Error> {
14        Ok(Box::new(self.built()?) as _)
15    }
16
17    fn log(&self, record: &Record) -> bool {
18        self.check(record.target(), record.level())
19    }
20
21    fn enabled(&self, metadata: &Metadata) -> bool {
22        self.check(metadata.target(), metadata.level())
23    }
24
25    fn maxlevel(&self) -> LevelFilter {
26        self.max_level
27    }
28}
29
30impl BaseFilter {
31    fn built(mut self) -> Result<Self, Error> {
32        // dbg!(&self);
33        self.filters.sort_by(|a, b| a.0.cmp(&b.0));
34        // dbg!(&self);
35
36        self.max_level = self.max_level_get();
37        let filters_length = self.filters.len();
38
39        let starts_with = self.starts_with;
40        self.filters
41            .dedup_by(|a, b| if starts_with { b.0.starts_with(&a.0) } else { b.0 == a.0 });
42
43        // dbg!(&self);
44
45        if filters_length > self.filters.len() {
46            Err("dedup token effect")?;
47        }
48
49        Ok(self)
50    }
51
52    fn check(&self, target: &str, level: Level) -> bool {
53        if let Some(idx) = self
54            .filters
55            .binary_search_by(|(t, _level)| {
56                if self.starts_with && target.starts_with(t) {
57                    Ordering::Equal
58                } else {
59                    t.as_str().cmp(target)
60                }
61            })
62            .ok()
63        {
64            unsafe { self.filters.get_unchecked(idx).1 >= level }
65        } else {
66            self.notfound
67        }
68    }
69}
70
71#[derive(Debug, Clone)]
72pub struct BaseFilter {
73    filters: Vec<(String, LevelFilter)>,
74    max_level: LevelFilter,
75    starts_with: bool,
76    notfound: bool,
77}
78
79impl Default for BaseFilter {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl BaseFilter {
86    pub fn new() -> Self {
87        Self {
88            filters: vec![],
89            max_level: LevelFilter::Trace,
90            starts_with: false,
91            notfound: true,
92        }
93    }
94
95    pub fn notfound(mut self, pass: bool) -> Self {
96        self.notfound = pass;
97        self
98    }
99
100    pub fn starts_with(mut self, yes: bool) -> Self {
101        self.starts_with = yes;
102        self
103    }
104
105    pub fn chain_iter<I>(mut self, filters: I) -> Self
106    where
107        I: IntoIterator<Item = (String, LevelFilter)>,
108    {
109        filters.into_iter().for_each(|tl| self.filters.push(tl));
110        self
111    }
112
113    pub fn chain<S: Into<String>>(mut self, target: S, level: LevelFilter) -> Self {
114        self.filters.push((target.into(), level));
115        self
116    }
117
118    pub fn max_level(mut self, max_level: LevelFilter) -> Self {
119        self.max_level = max_level;
120        self
121    }
122
123    // outer use as arg for Logger
124    pub fn max_level_get(&self) -> LevelFilter {
125        let has = self.max_level;
126        if let Some(compute) = self.filters.iter().max_by(|a, b| a.1.cmp(&b.1)) {
127            if self.notfound {
128                return max(compute.1, has);
129            } else {
130                return compute.1;
131            }
132        }
133        has
134    }
135
136    pub fn starts_with_get(&self) -> bool {
137        self.starts_with
138    }
139}