1use 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
75pub struct Filter {
83    directives: Vec<Directive>,
84    filter: Option<inner::Filter>,
85}
86
87pub 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    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    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    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    pub fn new() -> Builder {
174        Builder {
175            directives: HashMap::new(),
176            filter: None,
177            built: false,
178        }
179    }
180
181    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    pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
194        self.filter(Some(module), level)
195    }
196
197    pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
199        self.filter(None, level)
200    }
201
202    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    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    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            directives.push(Directive {
236                name: None,
237                level: LevelFilter::Error,
238            });
239        } else {
240            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            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
290fn 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                        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
361fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
363    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    #[test]
416    fn ensure_tests_cover_level_universe() {
417        let level_universe: Level = Level::Trace; 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    #[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(); 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        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        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        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        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        let (dirs, filter) = parse_spec(""); assert_eq!(dirs.len(), 0);
750        assert!(filter.is_none());
751    }
752
753    #[test]
754    fn parse_spec_blank_level_isolated() {
755        let (dirs, filter) = parse_spec("     "); assert_eq!(dirs.len(), 0);
759        assert!(filter.is_none());
760    }
761
762    #[test]
763    fn parse_spec_blank_level_isolated_comma_only() {
764        let (dirs, filter) = parse_spec(","); assert_eq!(dirs.len(), 0);
769        assert!(filter.is_none());
770    }
771
772    #[test]
773    fn parse_spec_blank_level_isolated_comma_blank() {
774        let (dirs, filter) = parse_spec(",     "); assert_eq!(dirs.len(), 0);
780        assert!(filter.is_none());
781    }
782
783    #[test]
784    fn parse_spec_blank_level_isolated_blank_comma() {
785        let (dirs, filter) = parse_spec("     ,"); assert_eq!(dirs.len(), 0);
791        assert!(filter.is_none());
792    }
793
794    #[test]
795    fn parse_spec_global() {
796        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        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        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        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}