1pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective};
2use crate::filter::{
3 directive::{DirectiveSet, Match},
4 env::{field, FieldMap},
5 level::LevelFilter,
6};
7use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
8use tracing_core::{span, Level, Metadata};
9
10#[derive(Debug, Eq, PartialEq, Clone)]
13#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
14pub struct Directive {
15 in_span: Option<String>,
16 fields: Vec<field::Match>,
17 pub(crate) target: Option<String>,
18 pub(crate) level: LevelFilter,
19}
20
21pub(super) type Dynamics = DirectiveSet<Directive>;
23
24pub(super) type Statics = DirectiveSet<StaticDirective>;
26
27pub(crate) type CallsiteMatcher = MatchSet<field::CallsiteMatch>;
28pub(crate) type SpanMatcher = MatchSet<field::SpanMatch>;
29
30#[derive(Debug, PartialEq, Eq)]
31pub(crate) struct MatchSet<T> {
32 field_matches: FilterVec<T>,
33 base_level: LevelFilter,
34}
35
36impl Directive {
37 pub(super) fn has_name(&self) -> bool {
38 self.in_span.is_some()
39 }
40
41 pub(super) fn has_fields(&self) -> bool {
42 !self.fields.is_empty()
43 }
44
45 pub(super) fn to_static(&self) -> Option<StaticDirective> {
46 if !self.is_static() {
47 return None;
48 }
49
50 let field_names = self.fields.iter().map(field::Match::name).collect();
53
54 Some(StaticDirective::new(
55 self.target.clone(),
56 field_names,
57 self.level,
58 ))
59 }
60
61 fn is_static(&self) -> bool {
62 !self.has_name() && !self.fields.iter().any(field::Match::has_value)
63 }
64
65 pub(super) fn is_dynamic(&self) -> bool {
66 self.has_name() || self.has_fields()
67 }
68
69 pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option<field::CallsiteMatch> {
70 let fieldset = meta.fields();
71 let fields = self
72 .fields
73 .iter()
74 .filter_map(
75 |field::Match {
76 ref name,
77 ref value,
78 }| {
79 if let Some(field) = fieldset.field(name) {
80 let value = value.as_ref().cloned()?;
81 Some(Ok((field, value)))
82 } else {
83 Some(Err(()))
84 }
85 },
86 )
87 .collect::<Result<FieldMap<_>, ()>>()
88 .ok()?;
89 Some(field::CallsiteMatch {
90 fields,
91 level: self.level,
92 })
93 }
94
95 pub(super) fn make_tables(
96 directives: impl IntoIterator<Item = Directive>,
97 ) -> (Dynamics, Statics) {
98 let (dyns, stats): (Vec<Directive>, Vec<Directive>) =
100 directives.into_iter().partition(Directive::is_dynamic);
101 let statics = stats
102 .into_iter()
103 .filter_map(|d| d.to_static())
104 .chain(dyns.iter().filter_map(Directive::to_static))
105 .collect();
106 (Dynamics::from_iter(dyns), statics)
107 }
108
109 pub(super) fn deregexify(&mut self) {
110 for field in &mut self.fields {
111 field.value = match field.value.take() {
112 Some(field::ValueMatch::Pat(pat)) => {
113 Some(field::ValueMatch::Debug(pat.into_debug_match()))
114 }
115 x => x,
116 }
117 }
118 }
119
120 pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> {
121 let mut cur = Self {
122 level: LevelFilter::TRACE,
123 target: None,
124 in_span: None,
125 fields: Vec::new(),
126 };
127
128 #[derive(Debug)]
129 enum ParseState {
130 Start,
131 LevelOrTarget { start: usize },
132 Span { span_start: usize },
133 Field { field_start: usize },
134 Fields,
135 Target,
136 Level { level_start: usize },
137 Complete,
138 }
139
140 use ParseState::*;
141 let mut state = Start;
142 for (i, c) in from.trim().char_indices() {
143 state = match (state, c) {
144 (Start, '[') => Span { span_start: i + 1 },
145 (Start, c) if !['-', ':', '_'].contains(&c) && !c.is_alphanumeric() => {
146 return Err(ParseError::new())
147 }
148 (Start, _) => LevelOrTarget { start: i },
149 (LevelOrTarget { start }, '=') => {
150 cur.target = Some(from[start..i].to_owned());
151 Level { level_start: i + 1 }
152 }
153 (LevelOrTarget { start }, '[') => {
154 cur.target = Some(from[start..i].to_owned());
155 Span { span_start: i + 1 }
156 }
157 (LevelOrTarget { start }, ',') => {
158 let (level, target) = match &from[start..] {
159 "" => (LevelFilter::TRACE, None),
160 level_or_target => match LevelFilter::from_str(level_or_target) {
161 Ok(level) => (level, None),
162 Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())),
163 },
164 };
165
166 cur.level = level;
167 cur.target = target;
168 Complete
169 }
170 (state @ LevelOrTarget { .. }, _) => state,
171 (Target, '=') => Level { level_start: i + 1 },
172 (Span { span_start }, ']') => {
173 cur.in_span = Some(from[span_start..i].to_owned());
174 Target
175 }
176 (Span { span_start }, '{') => {
177 cur.in_span = match &from[span_start..i] {
178 "" => None,
179 _ => Some(from[span_start..i].to_owned()),
180 };
181 Field { field_start: i + 1 }
182 }
183 (state @ Span { .. }, _) => state,
184 (Field { field_start }, '}') => {
185 cur.fields.push(match &from[field_start..i] {
186 "" => return Err(ParseError::new()),
187 field => field::Match::parse(field, regex)?,
188 });
189 Fields
190 }
191 (Field { field_start }, ',') => {
192 cur.fields.push(match &from[field_start..i] {
193 "" => return Err(ParseError::new()),
194 field => field::Match::parse(field, regex)?,
195 });
196 Field { field_start: i + 1 }
197 }
198 (state @ Field { .. }, _) => state,
199 (Fields, ']') => Target,
200 (Level { level_start }, ',') => {
201 cur.level = match &from[level_start..i] {
202 "" => LevelFilter::TRACE,
203 level => LevelFilter::from_str(level)?,
204 };
205 Complete
206 }
207 (state @ Level { .. }, _) => state,
208 _ => return Err(ParseError::new()),
209 };
210 }
211
212 match state {
213 LevelOrTarget { start } => {
214 let (level, target) = match &from[start..] {
215 "" => (LevelFilter::TRACE, None),
216 level_or_target => match LevelFilter::from_str(level_or_target) {
217 Ok(level) => (level, None),
218 Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())),
220 },
221 };
222
223 cur.level = level;
224 cur.target = target;
225 }
226 Level { level_start } => {
227 cur.level = match &from[level_start..] {
228 "" => LevelFilter::TRACE,
229 level => LevelFilter::from_str(level)?,
230 };
231 }
232 Target | Complete => {}
233 _ => return Err(ParseError::new()),
234 };
235
236 Ok(cur)
237 }
238}
239
240impl Match for Directive {
241 fn cares_about(&self, meta: &Metadata<'_>) -> bool {
242 if let Some(ref target) = self.target {
245 if !meta.target().starts_with(&target[..]) {
246 return false;
247 }
248 }
249
250 if let Some(ref name) = self.in_span {
253 if name != meta.name() {
254 return false;
255 }
256 }
257
258 let actual_fields = meta.fields();
260 for expected_field in &self.fields {
261 if actual_fields.field(&expected_field.name).is_none() {
263 return false;
264 }
265 }
266
267 true
268 }
269
270 fn level(&self) -> &LevelFilter {
271 &self.level
272 }
273}
274
275impl FromStr for Directive {
276 type Err = ParseError;
277 fn from_str(from: &str) -> Result<Self, Self::Err> {
278 Directive::parse(from, true)
279 }
280}
281
282impl Default for Directive {
283 fn default() -> Self {
284 Directive {
285 level: LevelFilter::OFF,
286 target: None,
287 in_span: None,
288 fields: Vec::new(),
289 }
290 }
291}
292
293impl PartialOrd for Directive {
294 fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
295 Some(self.cmp(other))
296 }
297}
298
299impl Ord for Directive {
300 fn cmp(&self, other: &Directive) -> Ordering {
301 let ordering = self
308 .target
309 .as_ref()
310 .map(String::len)
311 .cmp(&other.target.as_ref().map(String::len))
312 .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some()))
314 .then_with(|| self.fields.len().cmp(&other.fields.len()))
317 .then_with(|| {
322 self.target
323 .cmp(&other.target)
324 .then_with(|| self.in_span.cmp(&other.in_span))
325 .then_with(|| self.fields[..].cmp(&other.fields[..]))
326 })
327 .reverse();
328
329 #[cfg(debug_assertions)]
330 {
331 if ordering == Ordering::Equal {
332 debug_assert_eq!(
333 self.target, other.target,
334 "invariant violated: Ordering::Equal must imply a.target == b.target"
335 );
336 debug_assert_eq!(
337 self.in_span, other.in_span,
338 "invariant violated: Ordering::Equal must imply a.in_span == b.in_span"
339 );
340 debug_assert_eq!(
341 self.fields, other.fields,
342 "invariant violated: Ordering::Equal must imply a.fields == b.fields"
343 );
344 }
345 }
346
347 ordering
348 }
349}
350
351impl fmt::Display for Directive {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 let mut wrote_any = false;
354 if let Some(ref target) = self.target {
355 fmt::Display::fmt(target, f)?;
356 wrote_any = true;
357 }
358
359 if self.in_span.is_some() || !self.fields.is_empty() {
360 f.write_str("[")?;
361
362 if let Some(ref span) = self.in_span {
363 fmt::Display::fmt(span, f)?;
364 }
365
366 let mut fields = self.fields.iter();
367 if let Some(field) = fields.next() {
368 write!(f, "{{{}", field)?;
369 for field in fields {
370 write!(f, ",{}", field)?;
371 }
372 f.write_str("}")?;
373 }
374
375 f.write_str("]")?;
376 wrote_any = true;
377 }
378
379 if wrote_any {
380 f.write_str("=")?;
381 }
382
383 fmt::Display::fmt(&self.level, f)
384 }
385}
386
387impl From<LevelFilter> for Directive {
388 fn from(level: LevelFilter) -> Self {
389 Self {
390 level,
391 ..Self::default()
392 }
393 }
394}
395
396impl From<Level> for Directive {
397 fn from(level: Level) -> Self {
398 LevelFilter::from_level(level).into()
399 }
400}
401
402impl Dynamics {
405 pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher> {
406 let mut base_level = None;
407 let field_matches = self
408 .directives_for(metadata)
409 .filter_map(|d| {
410 if let Some(f) = d.field_matcher(metadata) {
411 return Some(f);
412 }
413 match base_level {
414 Some(ref b) if d.level > *b => base_level = Some(d.level),
415 None => base_level = Some(d.level),
416 _ => {}
417 }
418 None
419 })
420 .collect();
421
422 if let Some(base_level) = base_level {
423 Some(CallsiteMatcher {
424 field_matches,
425 base_level,
426 })
427 } else if !field_matches.is_empty() {
428 Some(CallsiteMatcher {
429 field_matches,
430 base_level: base_level.unwrap_or(LevelFilter::OFF),
431 })
432 } else {
433 None
434 }
435 }
436
437 pub(crate) fn has_value_filters(&self) -> bool {
438 self.directives()
439 .any(|d| d.fields.iter().any(|f| f.value.is_some()))
440 }
441}
442
443impl CallsiteMatcher {
446 pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher {
448 let field_matches = self
449 .field_matches
450 .iter()
451 .map(|m| {
452 let m = m.to_span_match();
453 attrs.record(&mut m.visitor());
454 m
455 })
456 .collect();
457 SpanMatcher {
458 field_matches,
459 base_level: self.base_level,
460 }
461 }
462}
463
464impl SpanMatcher {
465 pub(crate) fn level(&self) -> LevelFilter {
467 self.field_matches
468 .iter()
469 .filter_map(field::SpanMatch::filter)
470 .max()
471 .unwrap_or(self.base_level)
472 }
473
474 pub(crate) fn record_update(&self, record: &span::Record<'_>) {
475 for m in &self.field_matches {
476 record.record(&mut m.visitor())
477 }
478 }
479}
480
481#[cfg(test)]
482mod test {
483 use super::*;
484
485 fn parse_directives(dirs: impl AsRef<str>) -> Vec<Directive> {
486 dirs.as_ref()
487 .split(',')
488 .filter_map(|s| s.parse().ok())
489 .collect()
490 }
491
492 fn expect_parse(dirs: impl AsRef<str>) -> Vec<Directive> {
493 dirs.as_ref()
494 .split(',')
495 .map(|s| {
496 s.parse()
497 .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err))
498 })
499 .collect()
500 }
501
502 #[test]
503 fn directive_ordering_by_target_len() {
504 let mut dirs = expect_parse(
507 "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn",
508 );
509 dirs.sort_unstable();
510
511 let expected = vec![
512 "a_really_long_name_with_no_colons",
513 "foo::bar::baz",
514 "foo::bar",
515 "foo",
516 ];
517 let sorted = dirs
518 .iter()
519 .map(|d| d.target.as_ref().unwrap())
520 .collect::<Vec<_>>();
521
522 assert_eq!(expected, sorted);
523 }
524 #[test]
525 fn directive_ordering_by_span() {
526 let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn");
529 dirs.sort_unstable();
530
531 let expected = vec!["baz::quux", "bar", "foo", "a"];
532 let sorted = dirs
533 .iter()
534 .map(|d| d.target.as_ref().unwrap())
535 .collect::<Vec<_>>();
536
537 assert_eq!(expected, sorted);
538 }
539
540 #[test]
541 fn directive_ordering_uses_lexicographic_when_equal() {
542 let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info");
545 dirs.sort_unstable();
546
547 let expected = vec![
548 ("span", Some("b")),
549 ("span", Some("a")),
550 ("c", None),
551 ("b", None),
552 ("a", None),
553 ];
554 let sorted = dirs
555 .iter()
556 .map(|d| {
557 (
558 d.target.as_ref().unwrap().as_ref(),
559 d.in_span.as_ref().map(String::as_ref),
560 )
561 })
562 .collect::<Vec<_>>();
563
564 assert_eq!(expected, sorted);
565 }
566
567 #[test]
571 #[ignore]
572 fn directive_ordering_by_field_num() {
573 let mut dirs = expect_parse(
576 "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info"
577 );
578 dirs.sort_unstable();
579
580 let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"];
581 let sorted = dirs
582 .iter()
583 .map(|d| d.target.as_ref().unwrap())
584 .collect::<Vec<_>>();
585
586 assert_eq!(expected, sorted);
587 }
588
589 #[test]
590 fn parse_directives_ralith() {
591 let dirs = parse_directives("common=trace,server=trace");
592 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
593 assert_eq!(dirs[0].target, Some("common".to_string()));
594 assert_eq!(dirs[0].level, LevelFilter::TRACE);
595 assert_eq!(dirs[0].in_span, None);
596
597 assert_eq!(dirs[1].target, Some("server".to_string()));
598 assert_eq!(dirs[1].level, LevelFilter::TRACE);
599 assert_eq!(dirs[1].in_span, None);
600 }
601
602 #[test]
603 fn parse_directives_ralith_uc() {
604 let dirs = parse_directives("common=INFO,server=DEBUG");
605 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
606 assert_eq!(dirs[0].target, Some("common".to_string()));
607 assert_eq!(dirs[0].level, LevelFilter::INFO);
608 assert_eq!(dirs[0].in_span, None);
609
610 assert_eq!(dirs[1].target, Some("server".to_string()));
611 assert_eq!(dirs[1].level, LevelFilter::DEBUG);
612 assert_eq!(dirs[1].in_span, None);
613 }
614
615 #[test]
616 fn parse_directives_ralith_mixed() {
617 let dirs = parse_directives("common=iNfo,server=dEbUg");
618 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
619 assert_eq!(dirs[0].target, Some("common".to_string()));
620 assert_eq!(dirs[0].level, LevelFilter::INFO);
621 assert_eq!(dirs[0].in_span, None);
622
623 assert_eq!(dirs[1].target, Some("server".to_string()));
624 assert_eq!(dirs[1].level, LevelFilter::DEBUG);
625 assert_eq!(dirs[1].in_span, None);
626 }
627
628 #[test]
629 fn parse_directives_valid() {
630 let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
631 assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
632 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
633 assert_eq!(dirs[0].level, LevelFilter::ERROR);
634 assert_eq!(dirs[0].in_span, None);
635
636 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
637 assert_eq!(dirs[1].level, LevelFilter::TRACE);
638 assert_eq!(dirs[1].in_span, None);
639
640 assert_eq!(dirs[2].target, Some("crate2".to_string()));
641 assert_eq!(dirs[2].level, LevelFilter::DEBUG);
642 assert_eq!(dirs[2].in_span, None);
643
644 assert_eq!(dirs[3].target, Some("crate3".to_string()));
645 assert_eq!(dirs[3].level, LevelFilter::OFF);
646 assert_eq!(dirs[3].in_span, None);
647 }
648
649 #[test]
650
651 fn parse_level_directives() {
652 let dirs = parse_directives(
653 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
654 crate2=debug,crate3=trace,crate3::mod2::mod1=off",
655 );
656 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
657 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
658 assert_eq!(dirs[0].level, LevelFilter::ERROR);
659 assert_eq!(dirs[0].in_span, None);
660
661 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
662 assert_eq!(dirs[1].level, LevelFilter::WARN);
663 assert_eq!(dirs[1].in_span, None);
664
665 assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
666 assert_eq!(dirs[2].level, LevelFilter::INFO);
667 assert_eq!(dirs[2].in_span, None);
668
669 assert_eq!(dirs[3].target, Some("crate2".to_string()));
670 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
671 assert_eq!(dirs[3].in_span, None);
672
673 assert_eq!(dirs[4].target, Some("crate3".to_string()));
674 assert_eq!(dirs[4].level, LevelFilter::TRACE);
675 assert_eq!(dirs[4].in_span, None);
676
677 assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
678 assert_eq!(dirs[5].level, LevelFilter::OFF);
679 assert_eq!(dirs[5].in_span, None);
680 }
681
682 #[test]
683 fn parse_uppercase_level_directives() {
684 let dirs = parse_directives(
685 "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
686 crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
687 );
688 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
689 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
690 assert_eq!(dirs[0].level, LevelFilter::ERROR);
691 assert_eq!(dirs[0].in_span, None);
692
693 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
694 assert_eq!(dirs[1].level, LevelFilter::WARN);
695 assert_eq!(dirs[1].in_span, None);
696
697 assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
698 assert_eq!(dirs[2].level, LevelFilter::INFO);
699 assert_eq!(dirs[2].in_span, None);
700
701 assert_eq!(dirs[3].target, Some("crate2".to_string()));
702 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
703 assert_eq!(dirs[3].in_span, None);
704
705 assert_eq!(dirs[4].target, Some("crate3".to_string()));
706 assert_eq!(dirs[4].level, LevelFilter::TRACE);
707 assert_eq!(dirs[4].in_span, None);
708
709 assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
710 assert_eq!(dirs[5].level, LevelFilter::OFF);
711 assert_eq!(dirs[5].in_span, None);
712 }
713
714 #[test]
715 fn parse_numeric_level_directives() {
716 let dirs = parse_directives(
717 "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
718 crate3=5,crate3::mod2::mod1=0",
719 );
720 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
721 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
722 assert_eq!(dirs[0].level, LevelFilter::ERROR);
723 assert_eq!(dirs[0].in_span, None);
724
725 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
726 assert_eq!(dirs[1].level, LevelFilter::WARN);
727 assert_eq!(dirs[1].in_span, None);
728
729 assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
730 assert_eq!(dirs[2].level, LevelFilter::INFO);
731 assert_eq!(dirs[2].in_span, None);
732
733 assert_eq!(dirs[3].target, Some("crate2".to_string()));
734 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
735 assert_eq!(dirs[3].in_span, None);
736
737 assert_eq!(dirs[4].target, Some("crate3".to_string()));
738 assert_eq!(dirs[4].level, LevelFilter::TRACE);
739 assert_eq!(dirs[4].in_span, None);
740
741 assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
742 assert_eq!(dirs[5].level, LevelFilter::OFF);
743 assert_eq!(dirs[5].in_span, None);
744 }
745
746 #[test]
747 fn parse_directives_invalid_crate() {
748 let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug");
750 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
751 assert_eq!(dirs[0].target, Some("crate2".to_string()));
752 assert_eq!(dirs[0].level, LevelFilter::DEBUG);
753 assert_eq!(dirs[0].in_span, None);
754 }
755
756 #[test]
757 fn parse_directives_invalid_level() {
758 let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug");
760 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
761 assert_eq!(dirs[0].target, Some("crate2".to_string()));
762 assert_eq!(dirs[0].level, LevelFilter::DEBUG);
763 assert_eq!(dirs[0].in_span, None);
764 }
765
766 #[test]
767 fn parse_directives_string_level() {
768 let dirs = parse_directives("crate1::mod1=wrong,crate2=warn");
770 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
771 assert_eq!(dirs[0].target, Some("crate2".to_string()));
772 assert_eq!(dirs[0].level, LevelFilter::WARN);
773 assert_eq!(dirs[0].in_span, None);
774 }
775
776 #[test]
777 fn parse_directives_empty_level() {
778 let dirs = parse_directives("crate1::mod1=wrong,crate2=");
780 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
781 assert_eq!(dirs[0].target, Some("crate2".to_string()));
782 assert_eq!(dirs[0].level, LevelFilter::TRACE);
783 assert_eq!(dirs[0].in_span, None);
784 }
785
786 #[test]
787 fn parse_directives_global() {
788 let dirs = parse_directives("warn,crate2=debug");
790 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
791 assert_eq!(dirs[0].target, None);
792 assert_eq!(dirs[0].level, LevelFilter::WARN);
793 assert_eq!(dirs[1].in_span, None);
794
795 assert_eq!(dirs[1].target, Some("crate2".to_string()));
796 assert_eq!(dirs[1].level, LevelFilter::DEBUG);
797 assert_eq!(dirs[1].in_span, None);
798 }
799
800 fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) {
802 let dirs = parse_directives(directive_to_test);
803 assert_eq!(
804 dirs.len(),
805 1,
806 "\ninput: \"{}\"; parsed: {:#?}",
807 directive_to_test,
808 dirs
809 );
810 assert_eq!(dirs[0].target, None);
811 assert_eq!(dirs[0].level, level_expected);
812 assert_eq!(dirs[0].in_span, None);
813 }
814
815 #[test]
816 fn parse_directives_global_bare_warn_lc() {
817 test_parse_bare_level("warn", LevelFilter::WARN);
819 }
820
821 #[test]
822 fn parse_directives_global_bare_warn_uc() {
823 test_parse_bare_level("WARN", LevelFilter::WARN);
825 }
826
827 #[test]
828 fn parse_directives_global_bare_warn_mixed() {
829 test_parse_bare_level("wArN", LevelFilter::WARN);
831 }
832
833 #[test]
834 fn parse_directives_valid_with_spans() {
835 let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug");
836 assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs);
837 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
838 assert_eq!(dirs[0].level, LevelFilter::ERROR);
839 assert_eq!(dirs[0].in_span, Some("foo".to_string()));
840
841 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
842 assert_eq!(dirs[1].level, LevelFilter::TRACE);
843 assert_eq!(dirs[1].in_span, Some("bar".to_string()));
844
845 assert_eq!(dirs[2].target, Some("crate2".to_string()));
846 assert_eq!(dirs[2].level, LevelFilter::DEBUG);
847 assert_eq!(dirs[2].in_span, Some("baz".to_string()));
848 }
849
850 #[test]
851 fn parse_directives_with_dash_in_target_name() {
852 let dirs = parse_directives("target-name=info");
853 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
854 assert_eq!(dirs[0].target, Some("target-name".to_string()));
855 assert_eq!(dirs[0].level, LevelFilter::INFO);
856 assert_eq!(dirs[0].in_span, None);
857 }
858
859 #[test]
860 fn parse_directives_with_dash_in_span_name() {
861 let dirs = parse_directives("target[span-name]=info");
864 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
865 assert_eq!(dirs[0].target, Some("target".to_string()));
866 assert_eq!(dirs[0].level, LevelFilter::INFO);
867 assert_eq!(dirs[0].in_span, Some("span-name".to_string()));
868 }
869
870 #[test]
871 fn parse_directives_with_special_characters_in_span_name() {
872 let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}";
873
874 let dirs = parse_directives(format!("target[{}]=info", span_name));
875 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
876 assert_eq!(dirs[0].target, Some("target".to_string()));
877 assert_eq!(dirs[0].level, LevelFilter::INFO);
878 assert_eq!(dirs[0].in_span, Some(span_name.to_string()));
879 }
880
881 #[test]
882 fn parse_directives_with_invalid_span_chars() {
883 let invalid_span_name = "]{";
884
885 let dirs = parse_directives(format!("target[{}]=info", invalid_span_name));
886 assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs);
887 }
888}