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