1 //! Filtering for log records.
2 //!
3 //! This module contains the log filtering used by `env_logger` to match records.
4 //! You can use the `Filter` type in your own logger implementation to use the same
5 //! filter parsing and matching as `env_logger`. For more details about the format
6 //! for directive strings see [Enabling Logging].
7 //!
8 //! ## Using `env_logger` in your own logger
9 //!
10 //! You can use `env_logger`'s filtering functionality with your own logger.
11 //! Call [`Builder::parse`] to parse directives from a string when constructing
12 //! your logger. Call [`Filter::matches`] to check whether a record should be
13 //! logged based on the parsed filters when log records are received.
14 //!
15 //! ```
16 //! extern crate log;
17 //! extern crate env_logger;
18 //! use env_logger::filter::Filter;
19 //! use log::{Log, Metadata, Record};
20 //!
21 //! struct MyLogger {
22 //!     filter: Filter
23 //! }
24 //!
25 //! impl MyLogger {
26 //!     fn new() -> MyLogger {
27 //!         use env_logger::filter::Builder;
28 //!         let mut builder = Builder::new();
29 //!
30 //!         // Parse a directives string from an environment variable
31 //!         if let Ok(ref filter) = std::env::var("MY_LOG_LEVEL") {
32 //!            builder.parse(filter);
33 //!         }
34 //!
35 //!         MyLogger {
36 //!             filter: builder.build()
37 //!         }
38 //!     }
39 //! }
40 //!
41 //! impl Log for MyLogger {
42 //!     fn enabled(&self, metadata: &Metadata) -> bool {
43 //!         self.filter.enabled(metadata)
44 //!     }
45 //!
46 //!     fn log(&self, record: &Record) {
47 //!         // Check if the record is matched by the filter
48 //!         if self.filter.matches(record) {
49 //!             println!("{:?}", record);
50 //!         }
51 //!     }
52 //!
53 //!     fn flush(&self) {}
54 //! }
55 //! ```
56 //!
57 //! [Enabling Logging]: ../index.html#enabling-logging
58 //! [`Builder::parse`]: struct.Builder.html#method.parse
59 //! [`Filter::matches`]: struct.Filter.html#method.matches
60 
61 use log::{Level, LevelFilter, Metadata, Record};
62 use std::env;
63 use std::fmt;
64 use std::mem;
65 
66 #[cfg(feature = "regex")]
67 #[path = "regex.rs"]
68 mod inner;
69 
70 #[cfg(not(feature = "regex"))]
71 #[path = "string.rs"]
72 mod inner;
73 
74 /// A log filter.
75 ///
76 /// This struct can be used to determine whether or not a log record
77 /// should be written to the output.
78 /// Use the [`Builder`] type to parse and construct a `Filter`.
79 ///
80 /// [`Builder`]: struct.Builder.html
81 pub struct Filter {
82     directives: Vec<Directive>,
83     filter: Option<inner::Filter>,
84 }
85 
86 /// A builder for a log filter.
87 ///
88 /// It can be used to parse a set of directives from a string before building
89 /// a [`Filter`] instance.
90 ///
91 /// ## Example
92 ///
93 /// ```
94 /// # #[macro_use] extern crate log;
95 /// # use std::env;
96 /// use env_logger::filter::Builder;
97 ///
98 /// let mut builder = Builder::new();
99 ///
100 /// // Parse a logging filter from an environment variable.
101 /// if let Ok(rust_log) = env::var("RUST_LOG") {
102 ///     builder.parse(&rust_log);
103 /// }
104 ///
105 /// let filter = builder.build();
106 /// ```
107 ///
108 /// [`Filter`]: struct.Filter.html
109 pub struct Builder {
110     directives: Vec<Directive>,
111     filter: Option<inner::Filter>,
112     built: bool,
113 }
114 
115 #[derive(Debug)]
116 struct Directive {
117     name: Option<String>,
118     level: LevelFilter,
119 }
120 
121 impl Filter {
122     /// Returns the maximum `LevelFilter` that this filter instance is
123     /// configured to output.
124     ///
125     /// # Example
126     ///
127     /// ```rust
128     /// use log::LevelFilter;
129     /// use env_logger::filter::Builder;
130     ///
131     /// let mut builder = Builder::new();
132     /// builder.filter(Some("module1"), LevelFilter::Info);
133     /// builder.filter(Some("module2"), LevelFilter::Error);
134     ///
135     /// let filter = builder.build();
136     /// assert_eq!(filter.filter(), LevelFilter::Info);
137     /// ```
filter(&self) -> LevelFilter138     pub fn filter(&self) -> LevelFilter {
139         self.directives
140             .iter()
141             .map(|d| d.level)
142             .max()
143             .unwrap_or(LevelFilter::Off)
144     }
145 
146     /// Checks if this record matches the configured filter.
matches(&self, record: &Record) -> bool147     pub fn matches(&self, record: &Record) -> bool {
148         if !self.enabled(record.metadata()) {
149             return false;
150         }
151 
152         if let Some(filter) = self.filter.as_ref() {
153             if !filter.is_match(&*record.args().to_string()) {
154                 return false;
155             }
156         }
157 
158         true
159     }
160 
161     /// Determines if a log message with the specified metadata would be logged.
enabled(&self, metadata: &Metadata) -> bool162     pub fn enabled(&self, metadata: &Metadata) -> bool {
163         let level = metadata.level();
164         let target = metadata.target();
165 
166         enabled(&self.directives, level, target)
167     }
168 }
169 
170 impl Builder {
171     /// Initializes the filter builder with defaults.
new() -> Builder172     pub fn new() -> Builder {
173         Builder {
174             directives: Vec::new(),
175             filter: None,
176             built: false,
177         }
178     }
179 
180     /// Initializes the filter builder from an environment.
from_env(env: &str) -> Builder181     pub fn from_env(env: &str) -> Builder {
182         let mut builder = Builder::new();
183 
184         if let Ok(s) = env::var(env) {
185             builder.parse(&s);
186         }
187 
188         builder
189     }
190 
191     /// Adds a directive to the filter for a specific module.
filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self192     pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
193         self.filter(Some(module), level)
194     }
195 
196     /// Adds a directive to the filter for all modules.
filter_level(&mut self, level: LevelFilter) -> &mut Self197     pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
198         self.filter(None, level)
199     }
200 
201     /// Adds a directive to the filter.
202     ///
203     /// The given module (if any) will log at most the specified level provided.
204     /// If no module is provided then the filter will apply to all log messages.
filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self205     pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
206         self.directives.push(Directive {
207             name: module.map(|s| s.to_string()),
208             level,
209         });
210         self
211     }
212 
213     /// Parses the directives string.
214     ///
215     /// See the [Enabling Logging] section for more details.
216     ///
217     /// [Enabling Logging]: ../index.html#enabling-logging
parse(&mut self, filters: &str) -> &mut Self218     pub fn parse(&mut self, filters: &str) -> &mut Self {
219         let (directives, filter) = parse_spec(filters);
220 
221         self.filter = filter;
222 
223         for directive in directives {
224             self.directives.push(directive);
225         }
226         self
227     }
228 
229     /// Build a log filter.
build(&mut self) -> Filter230     pub fn build(&mut self) -> Filter {
231         assert!(!self.built, "attempt to re-use consumed builder");
232         self.built = true;
233 
234         if self.directives.is_empty() {
235             // Adds the default filter if none exist
236             self.directives.push(Directive {
237                 name: None,
238                 level: LevelFilter::Error,
239             });
240         } else {
241             // Sort the directives by length of their name, this allows a
242             // little more efficient lookup at runtime.
243             self.directives.sort_by(|a, b| {
244                 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
245                 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
246                 alen.cmp(&blen)
247             });
248         }
249 
250         Filter {
251             directives: mem::replace(&mut self.directives, Vec::new()),
252             filter: mem::replace(&mut self.filter, None),
253         }
254     }
255 }
256 
257 impl Default for Builder {
default() -> Self258     fn default() -> Self {
259         Builder::new()
260     }
261 }
262 
263 impl fmt::Debug for Filter {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result264     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265         f.debug_struct("Filter")
266             .field("filter", &self.filter)
267             .field("directives", &self.directives)
268             .finish()
269     }
270 }
271 
272 impl fmt::Debug for Builder {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result273     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
274         if self.built {
275             f.debug_struct("Filter").field("built", &true).finish()
276         } else {
277             f.debug_struct("Filter")
278                 .field("filter", &self.filter)
279                 .field("directives", &self.directives)
280                 .finish()
281         }
282     }
283 }
284 
285 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
286 /// and return a vector with log directives.
parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>)287 fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
288     let mut dirs = Vec::new();
289 
290     let mut parts = spec.split('/');
291     let mods = parts.next();
292     let filter = parts.next();
293     if parts.next().is_some() {
294         eprintln!(
295             "warning: invalid logging spec '{}', \
296              ignoring it (too many '/'s)",
297             spec
298         );
299         return (dirs, None);
300     }
301     if let Some(m) = mods {
302         for s in m.split(',').map(|ss| ss.trim()) {
303             if s.is_empty() {
304                 continue;
305             }
306             let mut parts = s.split('=');
307             let (log_level, name) =
308                 match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
309                     (Some(part0), None, None) => {
310                         // if the single argument is a log-level string or number,
311                         // treat that as a global fallback
312                         match part0.parse() {
313                             Ok(num) => (num, None),
314                             Err(_) => (LevelFilter::max(), Some(part0)),
315                         }
316                     }
317                     (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
318                     (Some(part0), Some(part1), None) => match part1.parse() {
319                         Ok(num) => (num, Some(part0)),
320                         _ => {
321                             eprintln!(
322                                 "warning: invalid logging spec '{}', \
323                                  ignoring it",
324                                 part1
325                             );
326                             continue;
327                         }
328                     },
329                     _ => {
330                         eprintln!(
331                             "warning: invalid logging spec '{}', \
332                              ignoring it",
333                             s
334                         );
335                         continue;
336                     }
337                 };
338             dirs.push(Directive {
339                 name: name.map(|s| s.to_string()),
340                 level: log_level,
341             });
342         }
343     }
344 
345     let filter = filter.and_then(|filter| match inner::Filter::new(filter) {
346         Ok(re) => Some(re),
347         Err(e) => {
348             eprintln!("warning: invalid regex filter - {}", e);
349             None
350         }
351     });
352 
353     (dirs, filter)
354 }
355 
356 // Check whether a level and target are enabled by the set of directives.
enabled(directives: &[Directive], level: Level, target: &str) -> bool357 fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
358     // Search for the longest match, the vector is assumed to be pre-sorted.
359     for directive in directives.iter().rev() {
360         match directive.name {
361             Some(ref name) if !target.starts_with(&**name) => {}
362             Some(..) | None => return level <= directive.level,
363         }
364     }
365     false
366 }
367 
368 #[cfg(test)]
369 mod tests {
370     use log::{Level, LevelFilter};
371 
372     use super::{enabled, parse_spec, Builder, Directive, Filter};
373 
make_logger_filter(dirs: Vec<Directive>) -> Filter374     fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
375         let mut logger = Builder::new().build();
376         logger.directives = dirs;
377         logger
378     }
379 
380     #[test]
filter_info()381     fn filter_info() {
382         let logger = Builder::new().filter(None, LevelFilter::Info).build();
383         assert!(enabled(&logger.directives, Level::Info, "crate1"));
384         assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
385     }
386 
387     #[test]
filter_beginning_longest_match()388     fn filter_beginning_longest_match() {
389         let logger = Builder::new()
390             .filter(Some("crate2"), LevelFilter::Info)
391             .filter(Some("crate2::mod"), LevelFilter::Debug)
392             .filter(Some("crate1::mod1"), LevelFilter::Warn)
393             .build();
394         assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
395         assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
396     }
397 
398     // Some of our tests are only correct or complete when they cover the full
399     // universe of variants for log::Level. In the unlikely event that a new
400     // variant is added in the future, this test will detect the scenario and
401     // alert us to the need to review and update the tests. In such a
402     // situation, this test will fail to compile, and the error message will
403     // look something like this:
404     //
405     //     error[E0004]: non-exhaustive patterns: `NewVariant` not covered
406     //        --> src/filter/mod.rs:413:15
407     //         |
408     //     413 |         match level_universe {
409     //         |               ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
410     #[test]
ensure_tests_cover_level_universe()411     fn ensure_tests_cover_level_universe() {
412         let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
413         match level_universe {
414             Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
415         }
416     }
417 
418     #[test]
parse_default()419     fn parse_default() {
420         let logger = Builder::new().parse("info,crate1::mod1=warn").build();
421         assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
422         assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
423     }
424 
425     #[test]
parse_default_bare_level_off_lc()426     fn parse_default_bare_level_off_lc() {
427         let logger = Builder::new().parse("off").build();
428         assert!(!enabled(&logger.directives, Level::Error, ""));
429         assert!(!enabled(&logger.directives, Level::Warn, ""));
430         assert!(!enabled(&logger.directives, Level::Info, ""));
431         assert!(!enabled(&logger.directives, Level::Debug, ""));
432         assert!(!enabled(&logger.directives, Level::Trace, ""));
433     }
434 
435     #[test]
parse_default_bare_level_off_uc()436     fn parse_default_bare_level_off_uc() {
437         let logger = Builder::new().parse("OFF").build();
438         assert!(!enabled(&logger.directives, Level::Error, ""));
439         assert!(!enabled(&logger.directives, Level::Warn, ""));
440         assert!(!enabled(&logger.directives, Level::Info, ""));
441         assert!(!enabled(&logger.directives, Level::Debug, ""));
442         assert!(!enabled(&logger.directives, Level::Trace, ""));
443     }
444 
445     #[test]
parse_default_bare_level_error_lc()446     fn parse_default_bare_level_error_lc() {
447         let logger = Builder::new().parse("error").build();
448         assert!(enabled(&logger.directives, Level::Error, ""));
449         assert!(!enabled(&logger.directives, Level::Warn, ""));
450         assert!(!enabled(&logger.directives, Level::Info, ""));
451         assert!(!enabled(&logger.directives, Level::Debug, ""));
452         assert!(!enabled(&logger.directives, Level::Trace, ""));
453     }
454 
455     #[test]
parse_default_bare_level_error_uc()456     fn parse_default_bare_level_error_uc() {
457         let logger = Builder::new().parse("ERROR").build();
458         assert!(enabled(&logger.directives, Level::Error, ""));
459         assert!(!enabled(&logger.directives, Level::Warn, ""));
460         assert!(!enabled(&logger.directives, Level::Info, ""));
461         assert!(!enabled(&logger.directives, Level::Debug, ""));
462         assert!(!enabled(&logger.directives, Level::Trace, ""));
463     }
464 
465     #[test]
parse_default_bare_level_warn_lc()466     fn parse_default_bare_level_warn_lc() {
467         let logger = Builder::new().parse("warn").build();
468         assert!(enabled(&logger.directives, Level::Error, ""));
469         assert!(enabled(&logger.directives, Level::Warn, ""));
470         assert!(!enabled(&logger.directives, Level::Info, ""));
471         assert!(!enabled(&logger.directives, Level::Debug, ""));
472         assert!(!enabled(&logger.directives, Level::Trace, ""));
473     }
474 
475     #[test]
parse_default_bare_level_warn_uc()476     fn parse_default_bare_level_warn_uc() {
477         let logger = Builder::new().parse("WARN").build();
478         assert!(enabled(&logger.directives, Level::Error, ""));
479         assert!(enabled(&logger.directives, Level::Warn, ""));
480         assert!(!enabled(&logger.directives, Level::Info, ""));
481         assert!(!enabled(&logger.directives, Level::Debug, ""));
482         assert!(!enabled(&logger.directives, Level::Trace, ""));
483     }
484 
485     #[test]
parse_default_bare_level_info_lc()486     fn parse_default_bare_level_info_lc() {
487         let logger = Builder::new().parse("info").build();
488         assert!(enabled(&logger.directives, Level::Error, ""));
489         assert!(enabled(&logger.directives, Level::Warn, ""));
490         assert!(enabled(&logger.directives, Level::Info, ""));
491         assert!(!enabled(&logger.directives, Level::Debug, ""));
492         assert!(!enabled(&logger.directives, Level::Trace, ""));
493     }
494 
495     #[test]
parse_default_bare_level_info_uc()496     fn parse_default_bare_level_info_uc() {
497         let logger = Builder::new().parse("INFO").build();
498         assert!(enabled(&logger.directives, Level::Error, ""));
499         assert!(enabled(&logger.directives, Level::Warn, ""));
500         assert!(enabled(&logger.directives, Level::Info, ""));
501         assert!(!enabled(&logger.directives, Level::Debug, ""));
502         assert!(!enabled(&logger.directives, Level::Trace, ""));
503     }
504 
505     #[test]
parse_default_bare_level_debug_lc()506     fn parse_default_bare_level_debug_lc() {
507         let logger = Builder::new().parse("debug").build();
508         assert!(enabled(&logger.directives, Level::Error, ""));
509         assert!(enabled(&logger.directives, Level::Warn, ""));
510         assert!(enabled(&logger.directives, Level::Info, ""));
511         assert!(enabled(&logger.directives, Level::Debug, ""));
512         assert!(!enabled(&logger.directives, Level::Trace, ""));
513     }
514 
515     #[test]
parse_default_bare_level_debug_uc()516     fn parse_default_bare_level_debug_uc() {
517         let logger = Builder::new().parse("DEBUG").build();
518         assert!(enabled(&logger.directives, Level::Error, ""));
519         assert!(enabled(&logger.directives, Level::Warn, ""));
520         assert!(enabled(&logger.directives, Level::Info, ""));
521         assert!(enabled(&logger.directives, Level::Debug, ""));
522         assert!(!enabled(&logger.directives, Level::Trace, ""));
523     }
524 
525     #[test]
parse_default_bare_level_trace_lc()526     fn parse_default_bare_level_trace_lc() {
527         let logger = Builder::new().parse("trace").build();
528         assert!(enabled(&logger.directives, Level::Error, ""));
529         assert!(enabled(&logger.directives, Level::Warn, ""));
530         assert!(enabled(&logger.directives, Level::Info, ""));
531         assert!(enabled(&logger.directives, Level::Debug, ""));
532         assert!(enabled(&logger.directives, Level::Trace, ""));
533     }
534 
535     #[test]
parse_default_bare_level_trace_uc()536     fn parse_default_bare_level_trace_uc() {
537         let logger = Builder::new().parse("TRACE").build();
538         assert!(enabled(&logger.directives, Level::Error, ""));
539         assert!(enabled(&logger.directives, Level::Warn, ""));
540         assert!(enabled(&logger.directives, Level::Info, ""));
541         assert!(enabled(&logger.directives, Level::Debug, ""));
542         assert!(enabled(&logger.directives, Level::Trace, ""));
543     }
544 
545     // In practice, the desired log level is typically specified by a token
546     // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
547     // 'TRACE'), but this tests serves as a reminder that
548     // log::Level::from_str() ignores all case variants.
549     #[test]
parse_default_bare_level_debug_mixed()550     fn parse_default_bare_level_debug_mixed() {
551         {
552             let logger = Builder::new().parse("Debug").build();
553             assert!(enabled(&logger.directives, Level::Error, ""));
554             assert!(enabled(&logger.directives, Level::Warn, ""));
555             assert!(enabled(&logger.directives, Level::Info, ""));
556             assert!(enabled(&logger.directives, Level::Debug, ""));
557             assert!(!enabled(&logger.directives, Level::Trace, ""));
558         }
559         {
560             let logger = Builder::new().parse("debuG").build();
561             assert!(enabled(&logger.directives, Level::Error, ""));
562             assert!(enabled(&logger.directives, Level::Warn, ""));
563             assert!(enabled(&logger.directives, Level::Info, ""));
564             assert!(enabled(&logger.directives, Level::Debug, ""));
565             assert!(!enabled(&logger.directives, Level::Trace, ""));
566         }
567         {
568             let logger = Builder::new().parse("deBug").build();
569             assert!(enabled(&logger.directives, Level::Error, ""));
570             assert!(enabled(&logger.directives, Level::Warn, ""));
571             assert!(enabled(&logger.directives, Level::Info, ""));
572             assert!(enabled(&logger.directives, Level::Debug, ""));
573             assert!(!enabled(&logger.directives, Level::Trace, ""));
574         }
575         {
576             let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
577             assert!(enabled(&logger.directives, Level::Error, ""));
578             assert!(enabled(&logger.directives, Level::Warn, ""));
579             assert!(enabled(&logger.directives, Level::Info, ""));
580             assert!(enabled(&logger.directives, Level::Debug, ""));
581             assert!(!enabled(&logger.directives, Level::Trace, ""));
582         }
583     }
584 
585     #[test]
match_full_path()586     fn match_full_path() {
587         let logger = make_logger_filter(vec![
588             Directive {
589                 name: Some("crate2".to_string()),
590                 level: LevelFilter::Info,
591             },
592             Directive {
593                 name: Some("crate1::mod1".to_string()),
594                 level: LevelFilter::Warn,
595             },
596         ]);
597         assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
598         assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
599         assert!(enabled(&logger.directives, Level::Info, "crate2"));
600         assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
601     }
602 
603     #[test]
no_match()604     fn no_match() {
605         let logger = make_logger_filter(vec![
606             Directive {
607                 name: Some("crate2".to_string()),
608                 level: LevelFilter::Info,
609             },
610             Directive {
611                 name: Some("crate1::mod1".to_string()),
612                 level: LevelFilter::Warn,
613             },
614         ]);
615         assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
616     }
617 
618     #[test]
match_beginning()619     fn match_beginning() {
620         let logger = make_logger_filter(vec![
621             Directive {
622                 name: Some("crate2".to_string()),
623                 level: LevelFilter::Info,
624             },
625             Directive {
626                 name: Some("crate1::mod1".to_string()),
627                 level: LevelFilter::Warn,
628             },
629         ]);
630         assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
631     }
632 
633     #[test]
match_beginning_longest_match()634     fn match_beginning_longest_match() {
635         let logger = make_logger_filter(vec![
636             Directive {
637                 name: Some("crate2".to_string()),
638                 level: LevelFilter::Info,
639             },
640             Directive {
641                 name: Some("crate2::mod".to_string()),
642                 level: LevelFilter::Debug,
643             },
644             Directive {
645                 name: Some("crate1::mod1".to_string()),
646                 level: LevelFilter::Warn,
647             },
648         ]);
649         assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
650         assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
651     }
652 
653     #[test]
match_default()654     fn match_default() {
655         let logger = make_logger_filter(vec![
656             Directive {
657                 name: None,
658                 level: LevelFilter::Info,
659             },
660             Directive {
661                 name: Some("crate1::mod1".to_string()),
662                 level: LevelFilter::Warn,
663             },
664         ]);
665         assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
666         assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
667     }
668 
669     #[test]
zero_level()670     fn zero_level() {
671         let logger = make_logger_filter(vec![
672             Directive {
673                 name: None,
674                 level: LevelFilter::Info,
675             },
676             Directive {
677                 name: Some("crate1::mod1".to_string()),
678                 level: LevelFilter::Off,
679             },
680         ]);
681         assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
682         assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
683     }
684 
685     #[test]
parse_spec_valid()686     fn parse_spec_valid() {
687         let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
688         assert_eq!(dirs.len(), 3);
689         assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
690         assert_eq!(dirs[0].level, LevelFilter::Error);
691 
692         assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
693         assert_eq!(dirs[1].level, LevelFilter::max());
694 
695         assert_eq!(dirs[2].name, Some("crate2".to_string()));
696         assert_eq!(dirs[2].level, LevelFilter::Debug);
697         assert!(filter.is_none());
698     }
699 
700     #[test]
parse_spec_invalid_crate()701     fn parse_spec_invalid_crate() {
702         // test parse_spec with multiple = in specification
703         let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
704         assert_eq!(dirs.len(), 1);
705         assert_eq!(dirs[0].name, Some("crate2".to_string()));
706         assert_eq!(dirs[0].level, LevelFilter::Debug);
707         assert!(filter.is_none());
708     }
709 
710     #[test]
parse_spec_invalid_level()711     fn parse_spec_invalid_level() {
712         // test parse_spec with 'noNumber' as log level
713         let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
714         assert_eq!(dirs.len(), 1);
715         assert_eq!(dirs[0].name, Some("crate2".to_string()));
716         assert_eq!(dirs[0].level, LevelFilter::Debug);
717         assert!(filter.is_none());
718     }
719 
720     #[test]
parse_spec_string_level()721     fn parse_spec_string_level() {
722         // test parse_spec with 'warn' as log level
723         let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
724         assert_eq!(dirs.len(), 1);
725         assert_eq!(dirs[0].name, Some("crate2".to_string()));
726         assert_eq!(dirs[0].level, LevelFilter::Warn);
727         assert!(filter.is_none());
728     }
729 
730     #[test]
parse_spec_empty_level()731     fn parse_spec_empty_level() {
732         // test parse_spec with '' as log level
733         let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
734         assert_eq!(dirs.len(), 1);
735         assert_eq!(dirs[0].name, Some("crate2".to_string()));
736         assert_eq!(dirs[0].level, LevelFilter::max());
737         assert!(filter.is_none());
738     }
739 
740     #[test]
parse_spec_empty_level_isolated()741     fn parse_spec_empty_level_isolated() {
742         // test parse_spec with "" as log level (and the entire spec str)
743         let (dirs, filter) = parse_spec(""); // should be ignored
744         assert_eq!(dirs.len(), 0);
745         assert!(filter.is_none());
746     }
747 
748     #[test]
parse_spec_blank_level_isolated()749     fn parse_spec_blank_level_isolated() {
750         // test parse_spec with a white-space-only string specified as the log
751         // level (and the entire spec str)
752         let (dirs, filter) = parse_spec("     "); // should be ignored
753         assert_eq!(dirs.len(), 0);
754         assert!(filter.is_none());
755     }
756 
757     #[test]
parse_spec_blank_level_isolated_comma_only()758     fn parse_spec_blank_level_isolated_comma_only() {
759         // The spec should contain zero or more comma-separated string slices,
760         // so a comma-only string should be interpretted as two empty strings
761         // (which should both be treated as invalid, so ignored).
762         let (dirs, filter) = parse_spec(","); // should be ignored
763         assert_eq!(dirs.len(), 0);
764         assert!(filter.is_none());
765     }
766 
767     #[test]
parse_spec_blank_level_isolated_comma_blank()768     fn parse_spec_blank_level_isolated_comma_blank() {
769         // The spec should contain zero or more comma-separated string slices,
770         // so this bogus spec should be interpretted as containing one empty
771         // string and one blank string. Both should both be treated as
772         // invalid, so ignored.
773         let (dirs, filter) = parse_spec(",     "); // should be ignored
774         assert_eq!(dirs.len(), 0);
775         assert!(filter.is_none());
776     }
777 
778     #[test]
parse_spec_blank_level_isolated_blank_comma()779     fn parse_spec_blank_level_isolated_blank_comma() {
780         // The spec should contain zero or more comma-separated string slices,
781         // so this bogus spec should be interpretted as containing one blank
782         // string and one empty string. Both should both be treated as
783         // invalid, so ignored.
784         let (dirs, filter) = parse_spec("     ,"); // should be ignored
785         assert_eq!(dirs.len(), 0);
786         assert!(filter.is_none());
787     }
788 
789     #[test]
parse_spec_global()790     fn parse_spec_global() {
791         // test parse_spec with no crate
792         let (dirs, filter) = parse_spec("warn,crate2=debug");
793         assert_eq!(dirs.len(), 2);
794         assert_eq!(dirs[0].name, None);
795         assert_eq!(dirs[0].level, LevelFilter::Warn);
796         assert_eq!(dirs[1].name, Some("crate2".to_string()));
797         assert_eq!(dirs[1].level, LevelFilter::Debug);
798         assert!(filter.is_none());
799     }
800 
801     #[test]
parse_spec_global_bare_warn_lc()802     fn parse_spec_global_bare_warn_lc() {
803         // test parse_spec with no crate, in isolation, all lowercase
804         let (dirs, filter) = parse_spec("warn");
805         assert_eq!(dirs.len(), 1);
806         assert_eq!(dirs[0].name, None);
807         assert_eq!(dirs[0].level, LevelFilter::Warn);
808         assert!(filter.is_none());
809     }
810 
811     #[test]
parse_spec_global_bare_warn_uc()812     fn parse_spec_global_bare_warn_uc() {
813         // test parse_spec with no crate, in isolation, all uppercase
814         let (dirs, filter) = parse_spec("WARN");
815         assert_eq!(dirs.len(), 1);
816         assert_eq!(dirs[0].name, None);
817         assert_eq!(dirs[0].level, LevelFilter::Warn);
818         assert!(filter.is_none());
819     }
820 
821     #[test]
parse_spec_global_bare_warn_mixed()822     fn parse_spec_global_bare_warn_mixed() {
823         // test parse_spec with no crate, in isolation, mixed case
824         let (dirs, filter) = parse_spec("wArN");
825         assert_eq!(dirs.len(), 1);
826         assert_eq!(dirs[0].name, None);
827         assert_eq!(dirs[0].level, LevelFilter::Warn);
828         assert!(filter.is_none());
829     }
830 
831     #[test]
parse_spec_valid_filter()832     fn parse_spec_valid_filter() {
833         let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
834         assert_eq!(dirs.len(), 3);
835         assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
836         assert_eq!(dirs[0].level, LevelFilter::Error);
837 
838         assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
839         assert_eq!(dirs[1].level, LevelFilter::max());
840 
841         assert_eq!(dirs[2].name, Some("crate2".to_string()));
842         assert_eq!(dirs[2].level, LevelFilter::Debug);
843         assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
844     }
845 
846     #[test]
parse_spec_invalid_crate_filter()847     fn parse_spec_invalid_crate_filter() {
848         let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
849         assert_eq!(dirs.len(), 1);
850         assert_eq!(dirs[0].name, Some("crate2".to_string()));
851         assert_eq!(dirs[0].level, LevelFilter::Debug);
852         assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
853     }
854 
855     #[test]
parse_spec_empty_with_filter()856     fn parse_spec_empty_with_filter() {
857         let (dirs, filter) = parse_spec("crate1/a*c");
858         assert_eq!(dirs.len(), 1);
859         assert_eq!(dirs[0].name, Some("crate1".to_string()));
860         assert_eq!(dirs[0].level, LevelFilter::max());
861         assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
862     }
863 }
864