1use log::{Level, LevelFilter, Metadata, Record};
62use std::env;
63use std::fmt;
64use std::mem;
65
66#[cfg(feature = "regex")]
67#[path = "regex.rs"]
68mod inner;
69
70#[cfg(not(feature = "regex"))]
71#[path = "string.rs"]
72mod inner;
73
74pub struct Builder {
98 directives: Vec<Directive>,
99 filter: Option<inner::Filter>,
100 built: bool,
101}
102
103impl Builder {
104 pub fn new() -> Builder {
106 Builder {
107 directives: Vec::new(),
108 filter: None,
109 built: false,
110 }
111 }
112
113 pub fn from_env(env: &str) -> Builder {
115 let mut builder = Builder::new();
116
117 if let Ok(s) = env::var(env) {
118 builder.parse(&s);
119 }
120
121 builder
122 }
123
124 fn insert_directive(&mut self, mut directive: Directive) {
126 if let Some(pos) = self
127 .directives
128 .iter()
129 .position(|d| d.name == directive.name)
130 {
131 mem::swap(&mut self.directives[pos], &mut directive);
132 } else {
133 self.directives.push(directive);
134 }
135 }
136
137 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
139 self.filter(Some(module), level)
140 }
141
142 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
144 self.filter(None, level)
145 }
146
147 pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
152 self.insert_directive(Directive {
153 name: module.map(|s| s.to_string()),
154 level,
155 });
156 self
157 }
158
159 pub fn parse(&mut self, filters: &str) -> &mut Self {
165 let (directives, filter) = parse_spec(filters);
166
167 self.filter = filter;
168
169 for directive in directives {
170 self.insert_directive(directive);
171 }
172 self
173 }
174
175 pub fn build(&mut self) -> Filter {
177 assert!(!self.built, "attempt to re-use consumed builder");
178 self.built = true;
179
180 let mut directives = Vec::new();
181 if self.directives.is_empty() {
182 directives.push(Directive {
184 name: None,
185 level: LevelFilter::Error,
186 });
187 } else {
188 directives = mem::take(&mut self.directives);
190 directives.sort_by(|a, b| {
193 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
194 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
195 alen.cmp(&blen)
196 });
197 }
198
199 Filter {
200 directives: mem::take(&mut directives),
201 filter: mem::take(&mut self.filter),
202 }
203 }
204}
205
206impl Default for Builder {
207 fn default() -> Self {
208 Builder::new()
209 }
210}
211
212impl fmt::Debug for Builder {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 if self.built {
215 f.debug_struct("Filter").field("built", &true).finish()
216 } else {
217 f.debug_struct("Filter")
218 .field("filter", &self.filter)
219 .field("directives", &self.directives)
220 .finish()
221 }
222 }
223}
224
225#[derive(Debug)]
226struct Directive {
227 name: Option<String>,
228 level: LevelFilter,
229}
230
231pub struct Filter {
239 directives: Vec<Directive>,
240 filter: Option<inner::Filter>,
241}
242
243impl Filter {
244 pub fn filter(&self) -> LevelFilter {
261 self.directives
262 .iter()
263 .map(|d| d.level)
264 .max()
265 .unwrap_or(LevelFilter::Off)
266 }
267
268 pub fn matches(&self, record: &Record) -> bool {
270 if !self.enabled(record.metadata()) {
271 return false;
272 }
273
274 if let Some(filter) = self.filter.as_ref() {
275 if !filter.is_match(&record.args().to_string()) {
276 return false;
277 }
278 }
279
280 true
281 }
282
283 pub fn enabled(&self, metadata: &Metadata) -> bool {
285 let level = metadata.level();
286 let target = metadata.target();
287
288 enabled(&self.directives, level, target)
289 }
290}
291
292impl fmt::Debug for Filter {
293 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294 f.debug_struct("Filter")
295 .field("filter", &self.filter)
296 .field("directives", &self.directives)
297 .finish()
298 }
299}
300
301fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
304 let mut dirs = Vec::new();
305
306 let mut parts = spec.split('/');
307 let mods = parts.next();
308 let filter = parts.next();
309 if parts.next().is_some() {
310 eprintln!(
311 "warning: invalid logging spec '{}', \
312 ignoring it (too many '/'s)",
313 spec
314 );
315 return (dirs, None);
316 }
317 if let Some(m) = mods {
318 for s in m.split(',').map(|ss| ss.trim()) {
319 if s.is_empty() {
320 continue;
321 }
322 let mut parts = s.split('=');
323 let (log_level, name) =
324 match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
325 (Some(part0), None, None) => {
326 match part0.parse() {
329 Ok(num) => (num, None),
330 Err(_) => (LevelFilter::max(), Some(part0)),
331 }
332 }
333 (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
334 (Some(part0), Some(part1), None) => match part1.parse() {
335 Ok(num) => (num, Some(part0)),
336 _ => {
337 eprintln!(
338 "warning: invalid logging spec '{}', \
339 ignoring it",
340 part1
341 );
342 continue;
343 }
344 },
345 _ => {
346 eprintln!(
347 "warning: invalid logging spec '{}', \
348 ignoring it",
349 s
350 );
351 continue;
352 }
353 };
354 dirs.push(Directive {
355 name: name.map(|s| s.to_string()),
356 level: log_level,
357 });
358 }
359 }
360
361 let filter = filter.and_then(|filter| match inner::Filter::new(filter) {
362 Ok(re) => Some(re),
363 Err(e) => {
364 eprintln!("warning: invalid regex filter - {}", e);
365 None
366 }
367 });
368
369 (dirs, filter)
370}
371
372fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
374 for directive in directives.iter().rev() {
376 match directive.name {
377 Some(ref name) if !target.starts_with(&**name) => {}
378 Some(..) | None => return level <= directive.level,
379 }
380 }
381 false
382}
383
384#[cfg(test)]
385mod tests {
386 use log::{Level, LevelFilter};
387
388 use super::{enabled, parse_spec, Builder, Directive, Filter};
389
390 fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
391 let mut logger = Builder::new().build();
392 logger.directives = dirs;
393 logger
394 }
395
396 #[test]
397 fn filter_info() {
398 let logger = Builder::new().filter(None, LevelFilter::Info).build();
399 assert!(enabled(&logger.directives, Level::Info, "crate1"));
400 assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
401 }
402
403 #[test]
404 fn filter_beginning_longest_match() {
405 let logger = Builder::new()
406 .filter(Some("crate2"), LevelFilter::Info)
407 .filter(Some("crate2::mod"), LevelFilter::Debug)
408 .filter(Some("crate1::mod1"), LevelFilter::Warn)
409 .build();
410 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
411 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
412 }
413
414 #[test]
427 fn ensure_tests_cover_level_universe() {
428 let level_universe: Level = Level::Trace; match level_universe {
430 Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
431 }
432 }
433
434 #[test]
435 fn parse_default() {
436 let logger = Builder::new().parse("info,crate1::mod1=warn").build();
437 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
438 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
439 }
440
441 #[test]
442 fn parse_default_bare_level_off_lc() {
443 let logger = Builder::new().parse("off").build();
444 assert!(!enabled(&logger.directives, Level::Error, ""));
445 assert!(!enabled(&logger.directives, Level::Warn, ""));
446 assert!(!enabled(&logger.directives, Level::Info, ""));
447 assert!(!enabled(&logger.directives, Level::Debug, ""));
448 assert!(!enabled(&logger.directives, Level::Trace, ""));
449 }
450
451 #[test]
452 fn parse_default_bare_level_off_uc() {
453 let logger = Builder::new().parse("OFF").build();
454 assert!(!enabled(&logger.directives, Level::Error, ""));
455 assert!(!enabled(&logger.directives, Level::Warn, ""));
456 assert!(!enabled(&logger.directives, Level::Info, ""));
457 assert!(!enabled(&logger.directives, Level::Debug, ""));
458 assert!(!enabled(&logger.directives, Level::Trace, ""));
459 }
460
461 #[test]
462 fn parse_default_bare_level_error_lc() {
463 let logger = Builder::new().parse("error").build();
464 assert!(enabled(&logger.directives, Level::Error, ""));
465 assert!(!enabled(&logger.directives, Level::Warn, ""));
466 assert!(!enabled(&logger.directives, Level::Info, ""));
467 assert!(!enabled(&logger.directives, Level::Debug, ""));
468 assert!(!enabled(&logger.directives, Level::Trace, ""));
469 }
470
471 #[test]
472 fn parse_default_bare_level_error_uc() {
473 let logger = Builder::new().parse("ERROR").build();
474 assert!(enabled(&logger.directives, Level::Error, ""));
475 assert!(!enabled(&logger.directives, Level::Warn, ""));
476 assert!(!enabled(&logger.directives, Level::Info, ""));
477 assert!(!enabled(&logger.directives, Level::Debug, ""));
478 assert!(!enabled(&logger.directives, Level::Trace, ""));
479 }
480
481 #[test]
482 fn parse_default_bare_level_warn_lc() {
483 let logger = Builder::new().parse("warn").build();
484 assert!(enabled(&logger.directives, Level::Error, ""));
485 assert!(enabled(&logger.directives, Level::Warn, ""));
486 assert!(!enabled(&logger.directives, Level::Info, ""));
487 assert!(!enabled(&logger.directives, Level::Debug, ""));
488 assert!(!enabled(&logger.directives, Level::Trace, ""));
489 }
490
491 #[test]
492 fn parse_default_bare_level_warn_uc() {
493 let logger = Builder::new().parse("WARN").build();
494 assert!(enabled(&logger.directives, Level::Error, ""));
495 assert!(enabled(&logger.directives, Level::Warn, ""));
496 assert!(!enabled(&logger.directives, Level::Info, ""));
497 assert!(!enabled(&logger.directives, Level::Debug, ""));
498 assert!(!enabled(&logger.directives, Level::Trace, ""));
499 }
500
501 #[test]
502 fn parse_default_bare_level_info_lc() {
503 let logger = Builder::new().parse("info").build();
504 assert!(enabled(&logger.directives, Level::Error, ""));
505 assert!(enabled(&logger.directives, Level::Warn, ""));
506 assert!(enabled(&logger.directives, Level::Info, ""));
507 assert!(!enabled(&logger.directives, Level::Debug, ""));
508 assert!(!enabled(&logger.directives, Level::Trace, ""));
509 }
510
511 #[test]
512 fn parse_default_bare_level_info_uc() {
513 let logger = Builder::new().parse("INFO").build();
514 assert!(enabled(&logger.directives, Level::Error, ""));
515 assert!(enabled(&logger.directives, Level::Warn, ""));
516 assert!(enabled(&logger.directives, Level::Info, ""));
517 assert!(!enabled(&logger.directives, Level::Debug, ""));
518 assert!(!enabled(&logger.directives, Level::Trace, ""));
519 }
520
521 #[test]
522 fn parse_default_bare_level_debug_lc() {
523 let logger = Builder::new().parse("debug").build();
524 assert!(enabled(&logger.directives, Level::Error, ""));
525 assert!(enabled(&logger.directives, Level::Warn, ""));
526 assert!(enabled(&logger.directives, Level::Info, ""));
527 assert!(enabled(&logger.directives, Level::Debug, ""));
528 assert!(!enabled(&logger.directives, Level::Trace, ""));
529 }
530
531 #[test]
532 fn parse_default_bare_level_debug_uc() {
533 let logger = Builder::new().parse("DEBUG").build();
534 assert!(enabled(&logger.directives, Level::Error, ""));
535 assert!(enabled(&logger.directives, Level::Warn, ""));
536 assert!(enabled(&logger.directives, Level::Info, ""));
537 assert!(enabled(&logger.directives, Level::Debug, ""));
538 assert!(!enabled(&logger.directives, Level::Trace, ""));
539 }
540
541 #[test]
542 fn parse_default_bare_level_trace_lc() {
543 let logger = Builder::new().parse("trace").build();
544 assert!(enabled(&logger.directives, Level::Error, ""));
545 assert!(enabled(&logger.directives, Level::Warn, ""));
546 assert!(enabled(&logger.directives, Level::Info, ""));
547 assert!(enabled(&logger.directives, Level::Debug, ""));
548 assert!(enabled(&logger.directives, Level::Trace, ""));
549 }
550
551 #[test]
552 fn parse_default_bare_level_trace_uc() {
553 let logger = Builder::new().parse("TRACE").build();
554 assert!(enabled(&logger.directives, Level::Error, ""));
555 assert!(enabled(&logger.directives, Level::Warn, ""));
556 assert!(enabled(&logger.directives, Level::Info, ""));
557 assert!(enabled(&logger.directives, Level::Debug, ""));
558 assert!(enabled(&logger.directives, Level::Trace, ""));
559 }
560
561 #[test]
566 fn parse_default_bare_level_debug_mixed() {
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();
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 let logger = Builder::new().parse("deBug").build();
585 assert!(enabled(&logger.directives, Level::Error, ""));
586 assert!(enabled(&logger.directives, Level::Warn, ""));
587 assert!(enabled(&logger.directives, Level::Info, ""));
588 assert!(enabled(&logger.directives, Level::Debug, ""));
589 assert!(!enabled(&logger.directives, Level::Trace, ""));
590 }
591 {
592 let logger = Builder::new().parse("DeBuG").build(); assert!(enabled(&logger.directives, Level::Error, ""));
594 assert!(enabled(&logger.directives, Level::Warn, ""));
595 assert!(enabled(&logger.directives, Level::Info, ""));
596 assert!(enabled(&logger.directives, Level::Debug, ""));
597 assert!(!enabled(&logger.directives, Level::Trace, ""));
598 }
599 }
600
601 #[test]
602 fn match_full_path() {
603 let logger = make_logger_filter(vec![
604 Directive {
605 name: Some("crate2".to_string()),
606 level: LevelFilter::Info,
607 },
608 Directive {
609 name: Some("crate1::mod1".to_string()),
610 level: LevelFilter::Warn,
611 },
612 ]);
613 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
614 assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
615 assert!(enabled(&logger.directives, Level::Info, "crate2"));
616 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
617 }
618
619 #[test]
620 fn no_match() {
621 let logger = make_logger_filter(vec![
622 Directive {
623 name: Some("crate2".to_string()),
624 level: LevelFilter::Info,
625 },
626 Directive {
627 name: Some("crate1::mod1".to_string()),
628 level: LevelFilter::Warn,
629 },
630 ]);
631 assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
632 }
633
634 #[test]
635 fn match_beginning() {
636 let logger = make_logger_filter(vec![
637 Directive {
638 name: Some("crate2".to_string()),
639 level: LevelFilter::Info,
640 },
641 Directive {
642 name: Some("crate1::mod1".to_string()),
643 level: LevelFilter::Warn,
644 },
645 ]);
646 assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
647 }
648
649 #[test]
650 fn match_beginning_longest_match() {
651 let logger = make_logger_filter(vec![
652 Directive {
653 name: Some("crate2".to_string()),
654 level: LevelFilter::Info,
655 },
656 Directive {
657 name: Some("crate2::mod".to_string()),
658 level: LevelFilter::Debug,
659 },
660 Directive {
661 name: Some("crate1::mod1".to_string()),
662 level: LevelFilter::Warn,
663 },
664 ]);
665 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
666 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
667 }
668
669 #[test]
670 fn match_default() {
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::Warn,
679 },
680 ]);
681 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
682 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
683 }
684
685 #[test]
686 fn zero_level() {
687 let logger = make_logger_filter(vec![
688 Directive {
689 name: None,
690 level: LevelFilter::Info,
691 },
692 Directive {
693 name: Some("crate1::mod1".to_string()),
694 level: LevelFilter::Off,
695 },
696 ]);
697 assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
698 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
699 }
700
701 #[test]
702 fn parse_spec_valid() {
703 let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
704 assert_eq!(dirs.len(), 3);
705 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
706 assert_eq!(dirs[0].level, LevelFilter::Error);
707
708 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
709 assert_eq!(dirs[1].level, LevelFilter::max());
710
711 assert_eq!(dirs[2].name, Some("crate2".to_string()));
712 assert_eq!(dirs[2].level, LevelFilter::Debug);
713 assert!(filter.is_none());
714 }
715
716 #[test]
717 fn parse_spec_invalid_crate() {
718 let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
720 assert_eq!(dirs.len(), 1);
721 assert_eq!(dirs[0].name, Some("crate2".to_string()));
722 assert_eq!(dirs[0].level, LevelFilter::Debug);
723 assert!(filter.is_none());
724 }
725
726 #[test]
727 fn parse_spec_invalid_level() {
728 let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
730 assert_eq!(dirs.len(), 1);
731 assert_eq!(dirs[0].name, Some("crate2".to_string()));
732 assert_eq!(dirs[0].level, LevelFilter::Debug);
733 assert!(filter.is_none());
734 }
735
736 #[test]
737 fn parse_spec_string_level() {
738 let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
740 assert_eq!(dirs.len(), 1);
741 assert_eq!(dirs[0].name, Some("crate2".to_string()));
742 assert_eq!(dirs[0].level, LevelFilter::Warn);
743 assert!(filter.is_none());
744 }
745
746 #[test]
747 fn parse_spec_empty_level() {
748 let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
750 assert_eq!(dirs.len(), 1);
751 assert_eq!(dirs[0].name, Some("crate2".to_string()));
752 assert_eq!(dirs[0].level, LevelFilter::max());
753 assert!(filter.is_none());
754 }
755
756 #[test]
757 fn parse_spec_empty_level_isolated() {
758 let (dirs, filter) = parse_spec(""); assert_eq!(dirs.len(), 0);
761 assert!(filter.is_none());
762 }
763
764 #[test]
765 fn parse_spec_blank_level_isolated() {
766 let (dirs, filter) = parse_spec(" "); assert_eq!(dirs.len(), 0);
770 assert!(filter.is_none());
771 }
772
773 #[test]
774 fn parse_spec_blank_level_isolated_comma_only() {
775 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_comma_blank() {
785 let (dirs, filter) = parse_spec(", "); assert_eq!(dirs.len(), 0);
791 assert!(filter.is_none());
792 }
793
794 #[test]
795 fn parse_spec_blank_level_isolated_blank_comma() {
796 let (dirs, filter) = parse_spec(" ,"); assert_eq!(dirs.len(), 0);
802 assert!(filter.is_none());
803 }
804
805 #[test]
806 fn parse_spec_global() {
807 let (dirs, filter) = parse_spec("warn,crate2=debug");
809 assert_eq!(dirs.len(), 2);
810 assert_eq!(dirs[0].name, None);
811 assert_eq!(dirs[0].level, LevelFilter::Warn);
812 assert_eq!(dirs[1].name, Some("crate2".to_string()));
813 assert_eq!(dirs[1].level, LevelFilter::Debug);
814 assert!(filter.is_none());
815 }
816
817 #[test]
818 fn parse_spec_global_bare_warn_lc() {
819 let (dirs, filter) = parse_spec("warn");
821 assert_eq!(dirs.len(), 1);
822 assert_eq!(dirs[0].name, None);
823 assert_eq!(dirs[0].level, LevelFilter::Warn);
824 assert!(filter.is_none());
825 }
826
827 #[test]
828 fn parse_spec_global_bare_warn_uc() {
829 let (dirs, filter) = parse_spec("WARN");
831 assert_eq!(dirs.len(), 1);
832 assert_eq!(dirs[0].name, None);
833 assert_eq!(dirs[0].level, LevelFilter::Warn);
834 assert!(filter.is_none());
835 }
836
837 #[test]
838 fn parse_spec_global_bare_warn_mixed() {
839 let (dirs, filter) = parse_spec("wArN");
841 assert_eq!(dirs.len(), 1);
842 assert_eq!(dirs[0].name, None);
843 assert_eq!(dirs[0].level, LevelFilter::Warn);
844 assert!(filter.is_none());
845 }
846
847 #[test]
848 fn parse_spec_valid_filter() {
849 let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
850 assert_eq!(dirs.len(), 3);
851 assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
852 assert_eq!(dirs[0].level, LevelFilter::Error);
853
854 assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
855 assert_eq!(dirs[1].level, LevelFilter::max());
856
857 assert_eq!(dirs[2].name, Some("crate2".to_string()));
858 assert_eq!(dirs[2].level, LevelFilter::Debug);
859 assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
860 }
861
862 #[test]
863 fn parse_spec_invalid_crate_filter() {
864 let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
865 assert_eq!(dirs.len(), 1);
866 assert_eq!(dirs[0].name, Some("crate2".to_string()));
867 assert_eq!(dirs[0].level, LevelFilter::Debug);
868 assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
869 }
870
871 #[test]
872 fn parse_spec_empty_with_filter() {
873 let (dirs, filter) = parse_spec("crate1/a*c");
874 assert_eq!(dirs.len(), 1);
875 assert_eq!(dirs[0].name, Some("crate1".to_string()));
876 assert_eq!(dirs[0].level, LevelFilter::max());
877 assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
878 }
879}