env_logger/filter/
mod.rs

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