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 #[allow(clippy::result_large_err)]
173 let value = parts
174 .next()
175 .map(|part| match regex {
176 true => ValueMatch::parse_regex(part),
177 false => Ok(ValueMatch::parse_non_regex(part)),
178 })
179 .transpose()?;
180 Ok(Match { name, value })
181 }
182}
183
184impl fmt::Display for Match {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 fmt::Display::fmt(&self.name, f)?;
187 if let Some(ref value) = self.value {
188 write!(f, "={}", value)?;
189 }
190 Ok(())
191 }
192}
193
194impl Ord for Match {
195 fn cmp(&self, other: &Self) -> Ordering {
196 let has_value = match (self.value.as_ref(), other.value.as_ref()) {
201 (Some(_), None) => Ordering::Greater,
202 (None, Some(_)) => Ordering::Less,
203 _ => Ordering::Equal,
204 };
205 has_value
213 .then_with(|| self.name.cmp(&other.name))
214 .then_with(|| self.value.cmp(&other.value))
215 }
216}
217
218impl PartialOrd for Match {
219 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
220 Some(self.cmp(other))
221 }
222}
223
224fn value_match_f64(v: f64) -> ValueMatch {
227 if v.is_nan() {
228 ValueMatch::NaN
229 } else {
230 ValueMatch::F64(v)
231 }
232}
233
234impl ValueMatch {
235 #[allow(clippy::result_large_err)]
242 fn parse_regex(s: &str) -> Result<Self, matchers::BuildError> {
243 s.parse::<bool>()
244 .map(ValueMatch::Bool)
245 .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
246 .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
247 .or_else(|_| s.parse::<f64>().map(value_match_f64))
248 .or_else(|_| {
249 s.parse::<MatchPattern>()
250 .map(|p| ValueMatch::Pat(Box::new(p)))
251 })
252 }
253
254 fn parse_non_regex(s: &str) -> Self {
261 s.parse::<bool>()
262 .map(ValueMatch::Bool)
263 .or_else(|_| s.parse::<u64>().map(ValueMatch::U64))
264 .or_else(|_| s.parse::<i64>().map(ValueMatch::I64))
265 .or_else(|_| s.parse::<f64>().map(value_match_f64))
266 .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s)))
267 }
268}
269
270impl fmt::Display for ValueMatch {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 match self {
273 ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f),
274 ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f),
275 ValueMatch::NaN => fmt::Display::fmt(&f64::NAN, f),
276 ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
277 ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
278 ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
279 ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f),
280 }
281 }
282}
283
284impl FromStr for MatchPattern {
287 type Err = matchers::BuildError;
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 let matcher = s.parse::<Pattern>()?;
290 Ok(Self {
291 matcher,
292 pattern: s.to_owned().into(),
293 })
294 }
295}
296
297impl fmt::Display for MatchPattern {
298 #[inline]
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 fmt::Display::fmt(&*self.pattern, f)
301 }
302}
303
304impl AsRef<str> for MatchPattern {
305 #[inline]
306 fn as_ref(&self) -> &str {
307 self.pattern.as_ref()
308 }
309}
310
311impl MatchPattern {
312 #[inline]
313 fn str_matches(&self, s: &impl AsRef<str>) -> bool {
314 self.matcher.matches(s)
315 }
316
317 #[inline]
318 fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
319 self.matcher.debug_matches(d)
320 }
321
322 pub(super) fn into_debug_match(self) -> MatchDebug {
323 MatchDebug {
324 pattern: self.pattern,
325 }
326 }
327}
328
329impl PartialEq for MatchPattern {
330 #[inline]
331 fn eq(&self, other: &Self) -> bool {
332 self.pattern == other.pattern
333 }
334}
335
336impl Eq for MatchPattern {}
337
338impl PartialOrd for MatchPattern {
339 #[inline]
340 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
341 Some(self.cmp(other))
342 }
343}
344
345impl Ord for MatchPattern {
346 #[inline]
347 fn cmp(&self, other: &Self) -> Ordering {
348 self.pattern.cmp(&other.pattern)
349 }
350}
351
352impl MatchDebug {
355 fn new(s: &str) -> Self {
356 Self {
357 pattern: s.to_owned().into(),
358 }
359 }
360
361 #[inline]
362 fn debug_matches(&self, d: &impl fmt::Debug) -> bool {
363 struct Matcher<'a> {
373 pattern: &'a str,
374 }
375
376 impl fmt::Write for Matcher<'_> {
377 fn write_str(&mut self, s: &str) -> fmt::Result {
378 if s.len() > self.pattern.len() {
381 return Err(fmt::Error);
382 }
383
384 if self.pattern.starts_with(s) {
389 self.pattern = &self.pattern[s.len()..];
390 return Ok(());
391 }
392
393 Err(fmt::Error)
398 }
399 }
400 let mut matcher = Matcher {
401 pattern: &self.pattern,
402 };
403
404 write!(matcher, "{:?}", d).is_ok()
408 }
409}
410
411impl fmt::Display for MatchDebug {
412 #[inline]
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 fmt::Display::fmt(&*self.pattern, f)
415 }
416}
417
418impl AsRef<str> for MatchDebug {
419 #[inline]
420 fn as_ref(&self) -> &str {
421 self.pattern.as_ref()
422 }
423}
424
425impl PartialEq for MatchDebug {
426 #[inline]
427 fn eq(&self, other: &Self) -> bool {
428 self.pattern == other.pattern
429 }
430}
431
432impl Eq for MatchDebug {}
433
434impl PartialOrd for MatchDebug {
435 #[inline]
436 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
437 Some(self.cmp(other))
438 }
439}
440
441impl Ord for MatchDebug {
442 #[inline]
443 fn cmp(&self, other: &Self) -> Ordering {
444 self.pattern.cmp(&other.pattern)
445 }
446}
447
448impl Error for BadName {}
451
452impl fmt::Display for BadName {
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 write!(f, "invalid field name `{}`", self.name)
455 }
456}
457
458impl CallsiteMatch {
459 pub(crate) fn to_span_match(&self) -> SpanMatch {
460 let fields = self
461 .fields
462 .iter()
463 .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false))))
464 .collect();
465 SpanMatch {
466 fields,
467 level: self.level,
468 has_matched: AtomicBool::new(false),
469 }
470 }
471}
472
473impl SpanMatch {
474 pub(crate) fn visitor(&self) -> MatchVisitor<'_> {
475 MatchVisitor { inner: self }
476 }
477
478 #[inline]
479 pub(crate) fn is_matched(&self) -> bool {
480 if self.has_matched.load(Acquire) {
481 return true;
482 }
483 self.is_matched_slow()
484 }
485
486 #[inline(never)]
487 fn is_matched_slow(&self) -> bool {
488 let matched = self
489 .fields
490 .values()
491 .all(|(_, matched)| matched.load(Acquire));
492 if matched {
493 self.has_matched.store(true, Release);
494 }
495 matched
496 }
497
498 #[inline]
499 pub(crate) fn filter(&self) -> Option<LevelFilter> {
500 if self.is_matched() {
501 Some(self.level)
502 } else {
503 None
504 }
505 }
506}
507
508impl Visit for MatchVisitor<'_> {
509 fn record_f64(&mut self, field: &Field, value: f64) {
510 match self.inner.fields.get(field) {
511 Some((ValueMatch::NaN, ref matched)) if value.is_nan() => {
512 matched.store(true, Release);
513 }
514 Some((ValueMatch::F64(ref e), ref matched)) if (value - *e).abs() < f64::EPSILON => {
515 matched.store(true, Release);
516 }
517 _ => {}
518 }
519 }
520
521 fn record_i64(&mut self, field: &Field, value: i64) {
522 use std::convert::TryInto;
523
524 match self.inner.fields.get(field) {
525 Some((ValueMatch::I64(ref e), ref matched)) if value == *e => {
526 matched.store(true, Release);
527 }
528 Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => {
529 matched.store(true, Release);
530 }
531 _ => {}
532 }
533 }
534
535 fn record_u64(&mut self, field: &Field, value: u64) {
536 match self.inner.fields.get(field) {
537 Some((ValueMatch::U64(ref e), ref matched)) if value == *e => {
538 matched.store(true, Release);
539 }
540 _ => {}
541 }
542 }
543
544 fn record_bool(&mut self, field: &Field, value: bool) {
545 match self.inner.fields.get(field) {
546 Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => {
547 matched.store(true, Release);
548 }
549 _ => {}
550 }
551 }
552
553 fn record_str(&mut self, field: &Field, value: &str) {
554 match self.inner.fields.get(field) {
555 Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => {
556 matched.store(true, Release);
557 }
558 Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
559 matched.store(true, Release)
560 }
561 _ => {}
562 }
563 }
564
565 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
566 match self.inner.fields.get(field) {
567 Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => {
568 matched.store(true, Release);
569 }
570 Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => {
571 matched.store(true, Release)
572 }
573 _ => {}
574 }
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use alloc::format;
582
583 #[derive(Debug)]
584 #[allow(dead_code)]
585 struct MyStruct {
586 answer: usize,
587 question: &'static str,
588 }
589
590 #[test]
591 fn debug_struct_match() {
592 let my_struct = MyStruct {
593 answer: 42,
594 question: "life, the universe, and everything",
595 };
596
597 let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
598
599 assert_eq!(
600 format!("{:?}", my_struct),
601 pattern,
602 "`MyStruct`'s `Debug` impl doesn't output the expected string"
603 );
604
605 let matcher = MatchDebug {
606 pattern: pattern.into(),
607 };
608 assert!(matcher.debug_matches(&my_struct))
609 }
610
611 #[test]
612 fn debug_struct_not_match() {
613 let my_struct = MyStruct {
614 answer: 42,
615 question: "what shall we have for lunch?",
616 };
617
618 let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }";
619
620 assert_eq!(
621 format!("{:?}", my_struct),
622 "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }",
623 "`MyStruct`'s `Debug` impl doesn't output the expected string"
624 );
625
626 let matcher = MatchDebug {
627 pattern: pattern.into(),
628 };
629 assert!(!matcher.debug_matches(&my_struct))
630 }
631}