diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/tracing-subscriber/src/filter/env/field.rs (renamed from vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs) | 250 |
1 files changed, 230 insertions, 20 deletions
diff --git a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs index 970850f92..1394fd04a 100644 --- a/vendor/tracing-subscriber-0.3.3/src/filter/env/field.rs +++ b/vendor/tracing-subscriber/src/filter/env/field.rs @@ -2,7 +2,7 @@ use matchers::Pattern; use std::{ cmp::Ordering, error::Error, - fmt, + fmt::{self, Write}, str::FromStr, sync::{ atomic::{AtomicBool, Ordering::*}, @@ -13,7 +13,7 @@ use std::{ use super::{FieldMap, LevelFilter}; use tracing_core::field::{Field, Visit}; -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Match { pub(crate) name: String, // TODO: allow match patterns for names? pub(crate) value: Option<ValueMatch>, @@ -38,11 +38,20 @@ pub(crate) struct MatchVisitor<'a> { #[derive(Debug, Clone)] pub(crate) enum ValueMatch { + /// Matches a specific `bool` value. Bool(bool), + /// Matches a specific `f64` value. F64(f64), + /// Matches a specific `u64` value. U64(u64), + /// Matches a specific `i64` value. I64(i64), + /// Matches any `NaN` `f64` value. NaN, + /// Matches any field whose `fmt::Debug` output is equal to a fixed string. + Debug(MatchDebug), + /// Matches any field whose `fmt::Debug` output matches a regular expression + /// pattern. Pat(Box<MatchPattern>), } @@ -97,6 +106,9 @@ impl Ord for ValueMatch { (Pat(this), Pat(that)) => this.cmp(that), (Pat(_), _) => Ordering::Greater, + + (Debug(this), Debug(that)) => this.cmp(that), + (Debug(_), _) => Ordering::Greater, } } } @@ -107,12 +119,25 @@ impl PartialOrd for ValueMatch { } } +/// Matches a field's `fmt::Debug` output against a regular expression pattern. +/// +/// This is used for matching all non-literal field value filters when regular +/// expressions are enabled. #[derive(Debug, Clone)] pub(crate) struct MatchPattern { pub(crate) matcher: Pattern, pattern: Arc<str>, } +/// Matches a field's `fmt::Debug` output against a fixed string pattern. +/// +/// This is used for matching all non-literal field value filters when regular +/// expressions are disabled. +#[derive(Debug, Clone)] +pub(crate) struct MatchDebug { + pattern: Arc<str>, +} + /// Indicates that a field name specified in a filter directive was invalid. #[derive(Clone, Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] @@ -122,9 +147,17 @@ pub struct BadName { // === impl Match === -impl FromStr for Match { - type Err = Box<dyn Error + Send + Sync>; - fn from_str(s: &str) -> Result<Self, Self::Err> { +impl Match { + pub(crate) fn has_value(&self) -> bool { + self.value.is_some() + } + + // TODO: reference count these strings? + pub(crate) fn name(&self) -> String { + self.name.clone() + } + + pub(crate) fn parse(s: &str, regex: bool) -> Result<Self, Box<dyn Error + Send + Sync>> { let mut parts = s.split('='); let name = parts .next() @@ -133,22 +166,17 @@ impl FromStr for Match { })? // TODO: validate field name .to_string(); - let value = parts.next().map(ValueMatch::from_str).transpose()?; + let value = parts + .next() + .map(|part| match regex { + true => ValueMatch::parse_regex(part), + false => Ok(ValueMatch::parse_non_regex(part)), + }) + .transpose()?; Ok(Match { name, value }) } } -impl Match { - pub(crate) fn has_value(&self) -> bool { - self.value.is_some() - } - - // TODO: reference count these strings? - pub(crate) fn name(&self) -> String { - self.name.clone() - } -} - impl fmt::Display for Match { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.name, f)?; @@ -199,9 +227,14 @@ fn value_match_f64(v: f64) -> ValueMatch { } } -impl FromStr for ValueMatch { - type Err = matchers::Error; - fn from_str(s: &str) -> Result<Self, Self::Err> { +impl ValueMatch { + /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular + /// expressions. + /// + /// This returns an error if the string didn't contain a valid `bool`, + /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular + /// expression. + fn parse_regex(s: &str) -> Result<Self, matchers::Error> { s.parse::<bool>() .map(ValueMatch::Bool) .or_else(|_| s.parse::<u64>().map(ValueMatch::U64)) @@ -212,6 +245,21 @@ impl FromStr for ValueMatch { .map(|p| ValueMatch::Pat(Box::new(p))) }) } + + /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed + /// string. + /// + /// This does *not* return an error, because any string that isn't a valid + /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected + /// `fmt::Debug` output. + fn parse_non_regex(s: &str) -> Self { + s.parse::<bool>() + .map(ValueMatch::Bool) + .or_else(|_| s.parse::<u64>().map(ValueMatch::U64)) + .or_else(|_| s.parse::<i64>().map(ValueMatch::I64)) + .or_else(|_| s.parse::<f64>().map(value_match_f64)) + .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s))) + } } impl fmt::Display for ValueMatch { @@ -222,6 +270,7 @@ impl fmt::Display for ValueMatch { ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f), ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f), ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f), + ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f), ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f), } } @@ -264,6 +313,12 @@ impl MatchPattern { fn debug_matches(&self, d: &impl fmt::Debug) -> bool { self.matcher.debug_matches(d) } + + pub(super) fn into_debug_match(self) -> MatchDebug { + MatchDebug { + pattern: self.pattern, + } + } } impl PartialEq for MatchPattern { @@ -289,6 +344,102 @@ impl Ord for MatchPattern { } } +// === impl MatchDebug === + +impl MatchDebug { + fn new(s: &str) -> Self { + Self { + pattern: s.to_owned().into(), + } + } + + #[inline] + fn debug_matches(&self, d: &impl fmt::Debug) -> bool { + // Naively, we would probably match a value's `fmt::Debug` output by + // formatting it to a string, and then checking if the string is equal + // to the expected pattern. However, this would require allocating every + // time we want to match a field value against a `Debug` matcher, which + // can be avoided. + // + // Instead, we implement `fmt::Write` for a type that, rather than + // actually _writing_ the strings to something, matches them against the + // expected pattern, and returns an error if the pattern does not match. + struct Matcher<'a> { + pattern: &'a str, + } + + impl fmt::Write for Matcher<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + // If the string is longer than the remaining expected string, + // we know it won't match, so bail. + if s.len() > self.pattern.len() { + return Err(fmt::Error); + } + + // If the expected string begins with the string that was + // written, we are still potentially a match. Advance the + // position in the expected pattern to chop off the matched + // output, and continue. + if self.pattern.starts_with(s) { + self.pattern = &self.pattern[s.len()..]; + return Ok(()); + } + + // Otherwise, the expected string doesn't include the string + // that was written at the current position, so the `fmt::Debug` + // output doesn't match! Return an error signalling that this + // doesn't match. + Err(fmt::Error) + } + } + let mut matcher = Matcher { + pattern: &self.pattern, + }; + + // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This + // returns an error if the `fmt::Debug` implementation wrote any + // characters that did not match the expected pattern. + write!(matcher, "{:?}", d).is_ok() + } +} + +impl fmt::Display for MatchDebug { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.pattern, f) + } +} + +impl AsRef<str> for MatchDebug { + #[inline] + fn as_ref(&self) -> &str { + self.pattern.as_ref() + } +} + +impl PartialEq for MatchDebug { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.pattern == other.pattern + } +} + +impl Eq for MatchDebug {} + +impl PartialOrd for MatchDebug { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.pattern.cmp(&other.pattern)) + } +} + +impl Ord for MatchDebug { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.pattern.cmp(&other.pattern) + } +} + // === impl BadName === impl Error for BadName {} @@ -401,6 +552,9 @@ impl<'a> Visit for MatchVisitor<'a> { Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => { matched.store(true, Release); } + Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release) + } _ => {} } } @@ -410,7 +564,63 @@ impl<'a> Visit for MatchVisitor<'a> { Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => { matched.store(true, Release); } + Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { + matched.store(true, Release) + } _ => {} } } } + +#[cfg(test)] +mod tests { + use super::*; + #[derive(Debug)] + #[allow(dead_code)] + struct MyStruct { + answer: usize, + question: &'static str, + } + + #[test] + fn debug_struct_match() { + let my_struct = MyStruct { + answer: 42, + question: "life, the universe, and everything", + }; + + let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; + + assert_eq!( + format!("{:?}", my_struct), + pattern, + "`MyStruct`'s `Debug` impl doesn't output the expected string" + ); + + let matcher = MatchDebug { + pattern: pattern.into(), + }; + assert!(matcher.debug_matches(&my_struct)) + } + + #[test] + fn debug_struct_not_match() { + let my_struct = MyStruct { + answer: 42, + question: "what shall we have for lunch?", + }; + + let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; + + assert_eq!( + format!("{:?}", my_struct), + "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }", + "`MyStruct`'s `Debug` impl doesn't output the expected string" + ); + + let matcher = MatchDebug { + pattern: pattern.into(), + }; + assert!(!matcher.debug_matches(&my_struct)) + } +} |