1use alloc::{
2 borrow::ToOwned,
3 boxed::Box,
4 string::{String, ToString},
5 sync::Arc,
6};
7use core::{
8 cmp::Ordering,
9 fmt::{self, Write},
10 str::FromStr,
11 sync::atomic::{AtomicBool, Ordering::*},
12};
13use matchers::Pattern;
14use std::error::Error;
15
16use super::{FieldMap, LevelFilter};
17use tracing_core::field::{Field, Visit};
18
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub(crate) struct Match {
21 pub(crate) name: String, pub(crate) value: Option<ValueMatch>,
23}
24
25#[derive(Debug, Eq, PartialEq)]
26pub(crate) struct CallsiteMatch {
27 pub(crate) fields: FieldMap<ValueMatch>,
28 pub(crate) level: LevelFilter,
29}
30
31#[derive(Debug)]
32pub(crate) struct SpanMatch {
33 fields: FieldMap<(ValueMatch, AtomicBool)>,
34 level: LevelFilter,
35 has_matched: AtomicBool,
36}
37
38pub(crate) struct MatchVisitor<'a> {
39 inner: &'a SpanMatch,
40}
41
42#[derive(Debug, Clone)]
43pub(crate) enum ValueMatch {
44 Bool(bool),
46 F64(f64),
48 U64(u64),
50 I64(i64),
52 NaN,
54 Debug(MatchDebug),
56 Pat(Box<MatchPattern>),
59}
60
61impl Eq for ValueMatch {}
62
63impl PartialEq for ValueMatch {
64 fn eq(&self, other: &Self) -> bool {
65 use ValueMatch::*;
66 match (self, other) {
67 (Bool(a), Bool(b)) => a.eq(b),
68 (F64(a), F64(b)) => {
69 debug_assert!(!a.is_nan());
70 debug_assert!(!b.is_nan());
71
72 a.eq(b)
73 }
74 (U64(a), U64(b)) => a.eq(b),
75 (I64(a), I64(b)) => a.eq(b),
76 (NaN, NaN) => true,
77 (Pat(a), Pat(b)) => a.eq(b),
78 _ => false,
79 }
80 }
81}
82
83impl Ord for ValueMatch {
84 fn cmp(&self, other: &Self) -> Ordering {
85 use ValueMatch::*;
86 match (self, other) {
87 (Bool(this), Bool(that)) => this.cmp(that),
88 (Bool(_), _) => Ordering::Less,
89
90 (F64(this), F64(that)) => this
91 .partial_cmp(that)
92 .expect("`ValueMatch::F64` may not contain `NaN` values"),
93 (F64(_), Bool(_)) => Ordering::Greater,
94 (F64(_), _) => Ordering::Less,
95
96 (NaN, NaN) => Ordering::Equal,
97 (NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater,
98 (NaN, _) => Ordering::Less,
99
100 (U64(this), U64(that)) => this.cmp(that),
101 (U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater,
102 (U64(_), _) => Ordering::Less,
103
104 (I64(this), I64(that)) => this.cmp(that),
105 (I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => {
106 Ordering::Greater
107 }
108 (I64(_), _) => Ordering::Less,
109
110 (Pat(this), Pat(that)) => this.cmp(that),
111 (Pat(_), _) => Ordering::Greater,
112
113 (Debug(this), Debug(that)) => this.cmp(that),
114 (Debug(_), _) => Ordering::Greater,
115 }
116 }
117}
118
119impl PartialOrd for ValueMatch {
120 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
121 Some(self.cmp(other))
122 }
123}
124
125#[derive(Debug, Clone)]
130pub(crate) struct MatchPattern {
131 pub(crate) matcher: Pattern,
132 pattern: Arc<str>,
133}
134
135#[derive(Debug, Clone)]
140pub(crate) struct MatchDebug {
141 pattern: Arc<str>,
142}
143
144#[derive(Clone, Debug)]
146#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
147pub struct BadName {
148 name: String,
149}
150
151impl Match {
154 pub(crate) fn has_value(&self) -> bool {
155 self.value.is_some()
156 }
157
158 pub(crate) fn name(&self) -> String {
160 self.name.clone()
161 }
162
163 pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> {
164 let mut parts = s.split('=');
165 let name = parts
166 .next()
167 .ok_or_else(|| BadName {
168 name: "".to_string(),
169 })?
170 .to_string();
172 let value = parts
173 .next()
174 .map(|part| match regex {
175 true => ValueMatch::parse_regex(part),
176 false => Ok(ValueMatch::parse_non_regex(part)),
177 })
178 .transpose()?;
179 Ok(Match { name, value })
180 }
181}
182
183impl fmt::Display for Match {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 fmt::Display::fmt(&self.name, f)?;
186 if let Some(ref value) = self.value {
187 write!(f, "={}", value)?;
188 }
189 Ok(())
190 }
191}
192
193impl Ord for Match {
194 fn cmp(&self, other: &Self) -> Ordering {
195 let has_value = match (self.value.as_ref(), other.value.as_ref()) {
200 (Some(_), None) => Ordering::Greater,
201 (None, Some(_)) => Ordering::Less,
202 _ => Ordering::Equal,
203 };
204 has_value
212 .then_with(|| self.name.cmp(&other.name))
213 .then_with(|| self.value.cmp(&other.value))
214 }
215}
216
217impl PartialOrd for Match {
218 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
219 Some(self.cmp(other))
220 }
221}
222
223fn value_match_f64(v: f64) -> ValueMatch {
226 if v.is_nan() {
227 ValueMatch::NaN
228 } else {
229 ValueMatch::F64(v)
230 }
231}
232
233impl ValueMatch {
234 #[allow(clippy::result_large_err)]
241 fn parse_regex(s: &str) -> Result<Self, matchers::BuildError> {
242 s.parse::<bool>()
243 .map(ValueMatch::Bool)
244 .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
245 .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
246 .or_else(|_| s.parse::<f64>().map(value_match_f64))
247 .or_else(|_| {
248 s.parse::<MatchPattern>()
249 .map(|p| ValueMatch::Pat(Box::new(p)))
250 })
251 }
252
253 fn parse_non_regex(s: &str) -> Self {
260 s.parse::<bool>()
261 .map(ValueMatch::Bool)
262 .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
263 .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
264 .or_else(|_| s.parse::<f64>().map(value_match_f64))
265 .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
266 }
267}
268
269impl fmt::Display for ValueMatch {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 match self {
272 ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f),
273 ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f),
274 ValueMatch::NaN => fmt::Display::fmt(&f64::NAN, f),
275 ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
276 ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
277 ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
278 ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
279 }
280 }
281}
282
283impl FromStr for MatchPattern {
286 type Err = matchers::BuildError;
287 fn from_str(s: &str) -> Result<Self, Self::Err> {
288 let matcher = s.parse::<Pattern>()?;
289 Ok(Self {
290 matcher,
291 pattern: s.to_owned().into(),
292 })
293 }
294}
295
296impl fmt::Display for MatchPattern {
297 #[inline]
298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299 fmt::Display::fmt(&*self.pattern, f)
300 }
301}
302
303impl AsRef<str> for MatchPattern {
304 #[inline]
305 fn as_ref(&self) -> &str {
306 self.pattern.as_ref()
307 }
308}
309
310impl MatchPattern {
311 #[inline]
312 fn str_matches(&self, s: &impl AsRef<str>) -> bool {
313 self.matcher.matches(s)
314 }
315
316 #[inline]
317 fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
318 self.matcher.debug_matches(d)
319 }
320
321 pub(super) fn into_debug_match(self) -> MatchDebug {
322 MatchDebug {
323 pattern: self.pattern,
324 }
325 }
326}
327
328impl PartialEq for MatchPattern {
329 #[inline]
330 fn eq(&self, other: &Self) -> bool {
331 self.pattern == other.pattern
332 }
333}
334
335impl Eq for MatchPattern {}
336
337impl PartialOrd for MatchPattern {
338 #[inline]
339 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
340 Some(self.cmp(other))
341 }
342}
343
344impl Ord for MatchPattern {
345 #[inline]
346 fn cmp(&self, other: &Self) -> Ordering {
347 self.pattern.cmp(&other.pattern)
348 }
349}
350
351impl MatchDebug {
354 fn new(s: &str) -> Self {
355 Self {
356 pattern: s.to_owned().into(),
357 }
358 }
359
360 #[inline]
361 fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
362 struct Matcher<'a> {
372 pattern: &'a str,
373 }
374
375 impl fmt::Write for Matcher<'_> {
376 fn write_str(&mut self, s: &str) -> fmt::Result {
377 if s.len() > self.pattern.len() {
380 return Err(fmt::Error);
381 }
382
383 if self.pattern.starts_with(s) {
388 self.pattern = &self.pattern[s.len()..];
389 return Ok(());
390 }
391
392 Err(fmt::Error)
397 }
398 }
399 let mut matcher = Matcher {
400 pattern: &self.pattern,
401 };
402
403 write!(matcher, "{:?}", d).is_ok()
407 }
408}
409
410impl fmt::Display for MatchDebug {
411 #[inline]
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 fmt::Display::fmt(&*self.pattern, f)
414 }
415}
416
417impl AsRef<str> for MatchDebug {
418 #[inline]
419 fn as_ref(&self) -> &str {
420 self.pattern.as_ref()
421 }
422}
423
424impl PartialEq for MatchDebug {
425 #[inline]
426 fn eq(&self, other: &Self) -> bool {
427 self.pattern == other.pattern
428 }
429}
430
431impl Eq for MatchDebug {}
432
433impl PartialOrd for MatchDebug {
434 #[inline]
435 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
436 Some(self.cmp(other))
437 }
438}
439
440impl Ord for MatchDebug {
441 #[inline]
442 fn cmp(&self, other: &Self) -> Ordering {
443 self.pattern.cmp(&other.pattern)
444 }
445}
446
447impl Error for BadName {}
450
451impl fmt::Display for BadName {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 write!(f, "invalid field name `{}`", self.name)
454 }
455}
456
457impl CallsiteMatch {
458 pub(crate) fn to_span_match(&self) -> SpanMatch {
459 let fields = self
460 .fields
461 .iter()
462 .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false))))
463 .collect();
464 SpanMatch {
465 fields,
466 level: self.level,
467 has_matched: AtomicBool::new(false),
468 }
469 }
470}
471
472impl SpanMatch {
473 pub(crate) fn visitor(&self) -> MatchVisitor<'_> {
474 MatchVisitor { inner: self }
475 }
476
477 #[inline]
478 pub(crate) fn is_matched(&self) -> bool {
479 if self.has_matched.load(Acquire) {
480 return true;
481 }
482 self.is_matched_slow()
483 }
484
485 #[inline(never)]
486 fn is_matched_slow(&self) -> bool {
487 let matched = self
488 .fields
489 .values()
490 .all(|(_, matched)| matched.load(Acquire));
491 if matched {
492 self.has_matched.store(true, Release);
493 }
494 matched
495 }
496
497 #[inline]
498 pub(crate) fn filter(&self) -> Option<LevelFilter> {
499 if self.is_matched() {
500 Some(self.level)
501 } else {
502 None
503 }
504 }
505}
506
507impl Visit for MatchVisitor<'_> {
508 fn record_f64(&mut self, field: &Field, value: f64) {
509 match self.inner.fields.get(field) {
510 Some((ValueMatch::NaN, ref matched)) if value.is_nan() => {
511 matched.store(true, Release);
512 }
513 Some((ValueMatch::F64(ref e), ref matched)) if (value - *e).abs() < f64::EPSILON => {
514 matched.store(true, Release);
515 }
516 _ => {}
517 }
518 }
519
520 fn record_i64(&mut self, field: &Field, value: i64) {
521 use std::convert::TryInto;
522
523 match self.inner.fields.get(field) {
524 Some((ValueMatch::I64(ref e), ref matched)) if value == *e => {
525 matched.store(true, Release);
526 }
527 Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => {
528 matched.store(true, Release);
529 }
530 _ => {}
531 }
532 }
533
534 fn record_u64(&mut self, field: &Field, value: u64) {
535 match self.inner.fields.get(field) {
536 Some((ValueMatch::U64(ref e), ref matched)) if value == *e => {
537 matched.store(true, Release);
538 }
539 _ => {}
540 }
541 }
542
543 fn record_bool(&mut self, field: &Field, value: bool) {
544 match self.inner.fields.get(field) {
545 Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => {
546 matched.store(true, Release);
547 }
548 _ => {}
549 }
550 }
551
552 fn record_str(&mut self, field: &Field, value: &str) {
553 match self.inner.fields.get(field) {
554 Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
555 matched.store(true, Release);
556 }
557 Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
558 matched.store(true, Release)
559 }
560 _ => {}
561 }
562 }
563
564 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
565 match self.inner.fields.get(field) {
566 Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
567 matched.store(true, Release);
568 }
569 Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
570 matched.store(true, Release)
571 }
572 _ => {}
573 }
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580 use alloc::format;
581
582 #[derive(Debug)]
583 #[allow(dead_code)]
584 struct MyStruct {
585 answer: usize,
586 question: &'static str,
587 }
588
589 #[test]
590 fn debug_struct_match() {
591 let my_struct = MyStruct {
592 answer: 42,
593 question: "life, the universe, and everything",
594 };
595
596 let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
597
598 assert_eq!(
599 format!("{:?}", my_struct),
600 pattern,
601 "`MyStruct`'s `Debug` impl doesn't output the expected string"
602 );
603
604 let matcher = MatchDebug {
605 pattern: pattern.into(),
606 };
607 assert!(matcher.debug_matches(&my_struct))
608 }
609
610 #[test]
611 fn debug_struct_not_match() {
612 let my_struct = MyStruct {
613 answer: 42,
614 question: "what shall we have for lunch?",
615 };
616
617 let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
618
619 assert_eq!(
620 format!("{:?}", my_struct),
621 "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
622 "`MyStruct`'s `Debug` impl doesn't output the expected string"
623 );
624
625 let matcher = MatchDebug {
626 pattern: pattern.into(),
627 };
628 assert!(!matcher.debug_matches(&my_struct))
629 }
630}