From 7c26d8db4d294a57898182a0550b0c21cfcbd2fe Mon Sep 17 00:00:00 2001 From: "Aode (lion)" Date: Wed, 27 Apr 2022 21:36:04 -0500 Subject: [PATCH] Make a String round-trippable Targets implementation --- .gitignore | 2 + Cargo.toml | 16 ++++ src/lib.rs | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ff593f0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tracing-subscriber-fmttargets" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +alloc = ["tracing-subscriber/alloc"] +default = ["registry", "std"] +registry = ["tracing-subscriber/registry"] +std = ["tracing-subscriber/std", "tracing-core/std"] + +[dependencies] +tracing-core = { version = "0.1", default-features = false } +tracing-subscriber = { version = "0.3", default-features = false } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fd1dce2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,237 @@ +#![cfg(any(feature = "std", feature = "alloc"))] + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct FmtTargets { + pub inner: tracing_subscriber::filter::Targets, +} + +impl FmtTargets { + pub fn new() -> Self { + Self { + inner: tracing_subscriber::filter::Targets::new(), + } + } + + pub fn with_target( + self, + target: impl Into, + level: impl Into, + ) -> Self { + Self { + inner: self.inner.with_target(target, level), + } + } + + pub fn with_targets(self, targets: impl IntoIterator) -> Self + where + String: From, + tracing_subscriber::filter::LevelFilter: From, + { + Self { + inner: self.inner.with_targets(targets), + } + } + + pub fn with_default(self, level: impl Into) -> Self { + Self { + inner: self.inner.with_default(level), + } + } + + pub fn iter(&self) -> tracing_subscriber::filter::targets::Iter<'_> { + self.inner.iter() + } + + pub fn would_enable(&self, target: &str, level: &tracing_core::metadata::Level) -> bool { + self.inner.would_enable(target, level) + } +} + +impl Extend<(T, L)> for FmtTargets +where + T: Into, + L: Into, +{ + fn extend>(&mut self, iter: I) { + self.inner.extend(iter) + } +} + +impl FromIterator<(T, L)> for FmtTargets +where + T: Into, + L: Into, +{ + fn from_iter>(iter: I) -> Self { + Self { + inner: FromIterator::from_iter(iter), + } + } +} + +impl IntoIterator for FmtTargets { + type Item = ::Item; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} + +impl<'a> IntoIterator for &'a FmtTargets { + type Item = <&'a tracing_subscriber::filter::Targets as IntoIterator>::Item; + type IntoIter = <&'a tracing_subscriber::filter::Targets as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + (&self.inner).into_iter() + } +} + +impl tracing_subscriber::Layer for FmtTargets +where + S: tracing_core::Subscriber, +{ + fn enabled( + &self, + metadata: &tracing_core::Metadata<'_>, + ctx: tracing_subscriber::layer::Context<'_, S>, + ) -> bool { + tracing_subscriber::Layer::::enabled(&self.inner, metadata, ctx) + } + + fn register_callsite( + &self, + metadata: &'static tracing_core::Metadata<'static>, + ) -> tracing_core::subscriber::Interest { + tracing_subscriber::Layer::::register_callsite(&self.inner, metadata) + } + + fn max_level_hint(&self) -> Option { + tracing_subscriber::Layer::::max_level_hint(&self.inner) + } +} + +#[cfg(feature = "registry")] +impl tracing_subscriber::layer::Filter for FmtTargets { + fn enabled( + &self, + meta: &tracing_core::Metadata<'_>, + cx: &tracing_subscriber::layer::Context<'_, S>, + ) -> bool { + tracing_subscriber::layer::Filter::::enabled(&self.inner, meta, cx) + } + + fn callsite_enabled( + &self, + meta: &'static tracing_core::Metadata<'static>, + ) -> tracing_core::subscriber::Interest { + tracing_subscriber::layer::Filter::::callsite_enabled(&self.inner, meta) + } + + fn max_level_hint(&self) -> Option { + tracing_subscriber::layer::Filter::::max_level_hint(&self.inner) + } +} + +impl AsRef for FmtTargets { + fn as_ref(&self) -> &tracing_subscriber::filter::Targets { + &self.inner + } +} + +impl AsMut for FmtTargets { + fn as_mut(&mut self) -> &mut tracing_subscriber::filter::Targets { + &mut self.inner + } +} + +impl From for FmtTargets { + fn from(inner: tracing_subscriber::filter::Targets) -> Self { + Self { inner } + } +} + +impl From for tracing_subscriber::filter::Targets { + fn from(this: FmtTargets) -> Self { + this.inner + } +} + +impl std::str::FromStr for FmtTargets { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + Ok(Self { inner: s.parse()? }) + } +} + +impl std::fmt::Display for FmtTargets { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let targets = self + .inner + .iter() + .map(|(path, level)| format!("{}={}", path, level)) + .collect::>() + .join(","); + + let max_level = [ + tracing_core::Level::TRACE, + tracing_core::Level::DEBUG, + tracing_core::Level::INFO, + tracing_core::Level::WARN, + tracing_core::Level::ERROR, + ] + .iter() + .fold(None, |found, level| { + if found.is_none() + && self + .inner + .would_enable("not_a_real_target_so_nothing_can_conflict", level) + { + Some(level.to_string().to_lowercase()) + } else { + found + } + }); + + if let Some(level) = max_level { + if !targets.is_empty() { + write!(f, "{},{}", level, targets) + } else { + write!(f, "{}", level) + } + } else if !targets.is_empty() { + write!(f, "{}", targets) + } else { + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use super::FmtTargets; + + #[test] + fn builds_info_targets() { + let t: FmtTargets = "info".parse().unwrap(); + + println!("{:?}", t); + + assert_eq!(t.to_string(), "info"); + } + + #[test] + fn builds_specific_targets() { + let t: FmtTargets = "pict_rs=info".parse().unwrap(); + + assert_eq!(t.to_string(), "pict_rs=info"); + } + + #[test] + fn builds_warn_and_specific_targets() { + let t: FmtTargets = "warn,pict_rs=info".parse().unwrap(); + + assert_eq!(t.to_string(), "warn,pict_rs=info"); + } +}