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