1use crate::{Level, Metadata};
7use std::{env, str::FromStr};
8
9pub struct FilterParseError;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
13pub enum LevelFilter {
14 Off,
15 Error,
16 Warn,
17 Info,
18 Debug,
19 Trace,
20}
21
22impl LevelFilter {
23 pub fn max() -> Self {
25 LevelFilter::Trace
26 }
27}
28
29impl FromStr for LevelFilter {
30 type Err = FilterParseError;
31
32 fn from_str(s: &str) -> Result<Self, Self::Err> {
33 let level = if s.eq_ignore_ascii_case("OFF") {
34 LevelFilter::Off
35 } else {
36 s.parse::<Level>().map_err(|_| FilterParseError)?.into()
37 };
38
39 Ok(level)
40 }
41}
42
43impl From<Level> for LevelFilter {
44 fn from(level: Level) -> Self {
45 match level {
46 Level::Error => LevelFilter::Error,
47 Level::Warn => LevelFilter::Warn,
48 Level::Info => LevelFilter::Info,
49 Level::Debug => LevelFilter::Debug,
50 Level::Trace => LevelFilter::Trace,
51 }
52 }
53}
54
55#[derive(Default, Debug)]
57pub struct Builder {
58 directives: Vec<Directive>,
59}
60
61impl Builder {
62 pub fn new() -> Self {
63 Default::default()
64 }
65
66 pub fn with_env(&mut self, env: &str) -> &mut Self {
68 if let Ok(s) = env::var(env) {
69 self.parse(&s);
70 }
71
72 self
73 }
74
75 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
77 self.filter(Some(module), level)
78 }
79
80 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
82 self.filter(None, level)
83 }
84
85 pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
90 self.directives.push(Directive::new(module, level));
91 self
92 }
93
94 pub fn parse(&mut self, filters: &str) -> &mut Self {
96 self.directives.extend(
97 filters
98 .split(',')
99 .map(Directive::from_str)
100 .filter_map(Result::ok),
101 );
102 self
103 }
104
105 pub fn build(&mut self) -> Filter {
106 if self.directives.is_empty() {
107 self.filter_level(LevelFilter::Error);
109 } else {
110 self.directives.sort_by(|a, b| {
113 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
114 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
115 alen.cmp(&blen)
116 });
117 }
118
119 Filter {
120 directives: ::std::mem::take(&mut self.directives),
121 }
122 }
123}
124
125#[derive(Debug)]
127pub struct Filter {
128 directives: Vec<Directive>,
129}
130
131impl Filter {
132 pub fn builder() -> Builder {
133 Builder::new()
134 }
135
136 pub fn enabled(&self, metadata: &Metadata) -> bool {
137 for directive in self.directives.iter().rev() {
139 match &directive.name {
140 Some(name) if !metadata.module_path().starts_with(name) => {}
141 Some(..) | None => return LevelFilter::from(metadata.level()) <= directive.level,
142 }
143 }
144 false
145 }
146}
147
148#[derive(Debug)]
150struct Directive {
151 name: Option<String>,
152 level: LevelFilter,
153}
154
155impl Directive {
156 fn new<T: Into<String>>(name: Option<T>, level: LevelFilter) -> Self {
157 Self {
158 name: name.map(Into::into),
159 level,
160 }
161 }
162}
163
164impl FromStr for Directive {
165 type Err = FilterParseError;
166
167 fn from_str(s: &str) -> Result<Self, Self::Err> {
168 let mut parts = s.split('=').map(str::trim);
169 let (name, level) = match (parts.next(), parts.next(), parts.next()) {
170 (Some(level_or_module), None, None) => match level_or_module.parse() {
172 Ok(level) => (None, level),
173 Err(_) => (Some(level_or_module), LevelFilter::max()),
174 },
175 (Some(name), Some(""), None) => (Some(name), LevelFilter::max()),
177 (Some(name), Some(level), None) => (Some(name), level.parse()?),
179 _ => return Err(FilterParseError),
180 };
181
182 Ok(Directive::new(name, level))
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::{Builder, Level, LevelFilter, Metadata};
189
190 fn make_metadata(level: Level, target: &'static str) -> Metadata {
191 Metadata::new(level, target, target, "")
192 }
193
194 #[test]
195 fn filter_info() {
196 let logger = Builder::new().filter_level(LevelFilter::Info).build();
197 assert!(logger.enabled(&make_metadata(Level::Info, "crate1")));
198 assert!(!logger.enabled(&make_metadata(Level::Debug, "crate1")));
199 }
200
201 #[test]
202 fn filter_beginning_longest_match() {
203 let logger = Builder::new()
204 .filter(Some("crate2"), LevelFilter::Info)
205 .filter(Some("crate2::mod"), LevelFilter::Debug)
206 .filter(Some("crate1::mod1"), LevelFilter::Warn)
207 .build();
208 assert!(logger.enabled(&make_metadata(Level::Debug, "crate2::mod1")));
209 assert!(!logger.enabled(&make_metadata(Level::Debug, "crate2")));
210 }
211
212 #[test]
213 fn parse_default() {
214 let logger = Builder::new().parse("info,crate1::mod1=warn").build();
215 assert!(logger.enabled(&make_metadata(Level::Warn, "crate1::mod1")));
216 assert!(logger.enabled(&make_metadata(Level::Info, "crate2::mod2")));
217 }
218
219 #[test]
220 fn match_full_path() {
221 let logger = Builder::new()
222 .filter(Some("crate2"), LevelFilter::Info)
223 .filter(Some("crate1::mod1"), LevelFilter::Warn)
224 .build();
225 assert!(logger.enabled(&make_metadata(Level::Warn, "crate1::mod1")));
226 assert!(!logger.enabled(&make_metadata(Level::Info, "crate1::mod1")));
227 assert!(logger.enabled(&make_metadata(Level::Info, "crate2")));
228 assert!(!logger.enabled(&make_metadata(Level::Debug, "crate2")));
229 }
230
231 #[test]
232 fn no_match() {
233 let logger = Builder::new()
234 .filter(Some("crate2"), LevelFilter::Info)
235 .filter(Some("crate1::mod1"), LevelFilter::Warn)
236 .build();
237 assert!(!logger.enabled(&make_metadata(Level::Warn, "crate3")));
238 }
239
240 #[test]
241 fn match_beginning() {
242 let logger = Builder::new()
243 .filter(Some("crate2"), LevelFilter::Info)
244 .filter(Some("crate1::mod1"), LevelFilter::Warn)
245 .build();
246 assert!(logger.enabled(&make_metadata(Level::Info, "crate2::mod1")));
247 }
248
249 #[test]
250 fn match_beginning_longest_match() {
251 let logger = Builder::new()
252 .filter(Some("crate2"), LevelFilter::Info)
253 .filter(Some("crate2::mod"), LevelFilter::Debug)
254 .filter(Some("crate1::mod1"), LevelFilter::Warn)
255 .build();
256 assert!(logger.enabled(&make_metadata(Level::Debug, "crate2::mod1")));
257 assert!(!logger.enabled(&make_metadata(Level::Debug, "crate2")));
258 }
259
260 #[test]
261 fn match_default() {
262 let logger = Builder::new()
263 .filter(None, LevelFilter::Info)
264 .filter(Some("crate1::mod1"), LevelFilter::Warn)
265 .build();
266 assert!(logger.enabled(&make_metadata(Level::Warn, "crate1::mod1")));
267 assert!(logger.enabled(&make_metadata(Level::Info, "crate2::mod2")));
268 }
269
270 #[test]
271 fn zero_level() {
272 let logger = Builder::new()
273 .filter(None, LevelFilter::Info)
274 .filter(Some("crate1::mod1"), LevelFilter::Off)
275 .build();
276 assert!(!logger.enabled(&make_metadata(Level::Error, "crate1::mod1")));
277 assert!(logger.enabled(&make_metadata(Level::Info, "crate2::mod2")));
278 }
279
280 #[test]
281 fn parse_valid() {
282 let mut builder = Builder::new();
283 builder.parse("crate1::mod1=error,crate1::mod2,crate2=debug");
284 let dirs = &builder.directives;
285
286 assert_eq!(dirs.len(), 3);
287 assert_eq!(dirs[0].name.as_deref(), Some("crate1::mod1"));
288 assert_eq!(dirs[0].level, LevelFilter::Error);
289
290 assert_eq!(dirs[1].name.as_deref(), Some("crate1::mod2"));
291 assert_eq!(dirs[1].level, LevelFilter::max());
292
293 assert_eq!(dirs[2].name.as_deref(), Some("crate2"));
294 assert_eq!(dirs[2].level, LevelFilter::Debug);
295 }
296
297 #[test]
298 fn parse_invalid_crate() {
299 let mut builder = Builder::new();
301 builder.parse("crate1::mod1=warn=info,crate2=debug");
302 let dirs = &builder.directives;
303
304 assert_eq!(dirs.len(), 1);
305 assert_eq!(dirs[0].name.as_deref(), Some("crate2"));
306 assert_eq!(dirs[0].level, LevelFilter::Debug);
307 }
308
309 #[test]
310 fn parse_invalid_level() {
311 let mut builder = Builder::new();
313 builder.parse("crate1::mod1=noNumber,crate2=debug");
314 let dirs = &builder.directives;
315 assert_eq!(dirs.len(), 1);
316 assert_eq!(dirs[0].name.as_deref(), Some("crate2"));
317 assert_eq!(dirs[0].level, LevelFilter::Debug);
318 }
319
320 #[test]
321 fn parse_string_level() {
322 let mut builder = Builder::new();
324 builder.parse("crate1::mod1=wrong,crate2=warn");
325 let dirs = &builder.directives;
326 assert_eq!(dirs.len(), 1);
327 assert_eq!(dirs[0].name.as_deref(), Some("crate2"));
328 assert_eq!(dirs[0].level, LevelFilter::Warn);
329 }
330
331 #[test]
332 fn parse_empty_level() {
333 let mut builder = Builder::new();
335 builder.parse("crate1::mod1=wrong,crate2=");
336 let dirs = &builder.directives;
337 assert_eq!(dirs.len(), 1);
338 assert_eq!(dirs[0].name.as_deref(), Some("crate2"));
339 assert_eq!(dirs[0].level, LevelFilter::max());
340 }
341
342 #[test]
343 fn parse_global() {
344 let mut builder = Builder::new();
346 builder.parse("warn,crate2=debug");
347 let dirs = &builder.directives;
348 assert_eq!(dirs.len(), 2);
349 assert_eq!(dirs[0].name.as_deref(), None);
350 assert_eq!(dirs[0].level, LevelFilter::Warn);
351 assert_eq!(dirs[1].name.as_deref(), Some("crate2"));
352 assert_eq!(dirs[1].level, LevelFilter::Debug);
353 }
354}