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::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
74/// A builder for a log filter.
75///
76/// It can be used to parse a set of directives from a string before building
77/// a [`Filter`] instance.
78///
79/// ## Example
80///
81/// ```
82/// # #[macro_use] extern crate log;
83/// # use std::env;
84/// use env_logger::filter::Builder;
85///
86/// let mut builder = Builder::new();
87///
88/// // Parse a logging filter from an environment variable.
89/// if let Ok(rust_log) = env::var("RUST_LOG") {
90/// builder.parse(&rust_log);
91/// }
92///
93/// let filter = builder.build();
94/// ```
95///
96/// [`Filter`]: struct.Filter.html
97pub struct Builder {
98 directives: Vec<Directive>,
99 filter: Option<inner::Filter>,
100 built: bool,
101}
102
103impl Builder {
104 /// Initializes the filter builder with defaults.
105 pub fn new() -> Builder {
106 Builder {
107 directives: Vec::new(),
108 filter: None,
109 built: false,
110 }
111 }
112
113 /// Initializes the filter builder from an environment.
114 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 /// Insert the directive replacing any directive with the same name.
125 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 /// Adds a directive to the filter for a specific module.
138 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
139 self.filter(Some(module), level)
140 }
141
142 /// Adds a directive to the filter for all modules.
143 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
144 self.filter(None, level)
145 }
146
147 /// Adds a directive to the filter.
148 ///
149 /// The given module (if any) will log at most the specified level provided.
150 /// If no module is provided then the filter will apply to all log messages.
151 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 /// Parses the directives string.
160 ///
161 /// See the [Enabling Logging] section for more details.
162 ///
163 /// [Enabling Logging]: ../index.html#enabling-logging
164 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 /// Build a log filter.
176 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 // Adds the default filter if none exist
183 directives.push(Directive {
184 name: None,
185 level: LevelFilter::Error,
186 });
187 } else {
188 // Consume directives.
189 directives = mem::take(&mut self.directives);
190 // Sort the directives by length of their name, this allows a
191 // little more efficient lookup at runtime.
192 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(name:"built", &true).finish()
216 } else {
217 f&mut DebugStruct<'_, '_>.debug_struct("Filter")
218 .field("filter", &self.filter)
219 .field(name:"directives", &self.directives)
220 .finish()
221 }
222 }
223}
224
225#[derive(Debug)]
226struct Directive {
227 name: Option<String>,
228 level: LevelFilter,
229}
230
231/// A log filter.
232///
233/// This struct can be used to determine whether or not a log record
234/// should be written to the output.
235/// Use the [`Builder`] type to parse and construct a `Filter`.
236///
237/// [`Builder`]: struct.Builder.html
238pub struct Filter {
239 directives: Vec<Directive>,
240 filter: Option<inner::Filter>,
241}
242
243impl Filter {
244 /// Returns the maximum `LevelFilter` that this filter instance is
245 /// configured to output.
246 ///
247 /// # Example
248 ///
249 /// ```rust
250 /// use log::LevelFilter;
251 /// use env_logger::filter::Builder;
252 ///
253 /// let mut builder = Builder::new();
254 /// builder.filter(Some("module1"), LevelFilter::Info);
255 /// builder.filter(Some("module2"), LevelFilter::Error);
256 ///
257 /// let filter = builder.build();
258 /// assert_eq!(filter.filter(), LevelFilter::Info);
259 /// ```
260 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 /// Checks if this record matches the configured filter.
269 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 /// Determines if a log message with the specified metadata would be logged.
284 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&mut DebugStruct<'_, '_>.debug_struct("Filter")
295 .field("filter", &self.filter)
296 .field(name:"directives", &self.directives)
297 .finish()
298 }
299}
300
301/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
302/// and return a vector with log directives.
303fn 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 // if the single argument is a log-level string or number,
327 // treat that as a global fallback
328 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
372// Check whether a level and target are enabled by the set of directives.
373fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
374 // Search for the longest match, the vector is assumed to be pre-sorted.
375 for directive: &Directive in directives.iter().rev() {
376 match directive.name {
377 Some(ref name: &String) 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 // Some of our tests are only correct or complete when they cover the full
415 // universe of variants for log::Level. In the unlikely event that a new
416 // variant is added in the future, this test will detect the scenario and
417 // alert us to the need to review and update the tests. In such a
418 // situation, this test will fail to compile, and the error message will
419 // look something like this:
420 //
421 // error[E0004]: non-exhaustive patterns: `NewVariant` not covered
422 // --> src/filter/mod.rs:413:15
423 // |
424 // 413 | match level_universe {
425 // | ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
426 #[test]
427 fn ensure_tests_cover_level_universe() {
428 let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
429 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 // In practice, the desired log level is typically specified by a token
562 // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
563 // 'TRACE'), but this tests serves as a reminder that
564 // log::Level::from_str() ignores all case variants.
565 #[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(); // LaTeX flavor!
593 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 // test parse_spec with multiple = in specification
719 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 // test parse_spec with 'noNumber' as log level
729 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 // test parse_spec with 'warn' as log level
739 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 // test parse_spec with '' as log level
749 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 // test parse_spec with "" as log level (and the entire spec str)
759 let (dirs, filter) = parse_spec(""); // should be ignored
760 assert_eq!(dirs.len(), 0);
761 assert!(filter.is_none());
762 }
763
764 #[test]
765 fn parse_spec_blank_level_isolated() {
766 // test parse_spec with a white-space-only string specified as the log
767 // level (and the entire spec str)
768 let (dirs, filter) = parse_spec(" "); // should be ignored
769 assert_eq!(dirs.len(), 0);
770 assert!(filter.is_none());
771 }
772
773 #[test]
774 fn parse_spec_blank_level_isolated_comma_only() {
775 // The spec should contain zero or more comma-separated string slices,
776 // so a comma-only string should be interpreted as two empty strings
777 // (which should both be treated as 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_comma_blank() {
785 // The spec should contain zero or more comma-separated string slices,
786 // so this bogus spec should be interpreted as containing one empty
787 // string and one blank 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_blank_level_isolated_blank_comma() {
796 // The spec should contain zero or more comma-separated string slices,
797 // so this bogus spec should be interpreted as containing one blank
798 // string and one empty string. Both should both be treated as
799 // invalid, so ignored.
800 let (dirs, filter) = parse_spec(" ,"); // should be ignored
801 assert_eq!(dirs.len(), 0);
802 assert!(filter.is_none());
803 }
804
805 #[test]
806 fn parse_spec_global() {
807 // test parse_spec with no crate
808 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 // test parse_spec with no crate, in isolation, all lowercase
820 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 // test parse_spec with no crate, in isolation, all uppercase
830 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 // test parse_spec with no crate, in isolation, mixed case
840 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}
880