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