Make a String round-trippable Targets implementation

This commit is contained in:
Aode (lion) 2022-04-27 21:36:04 -05:00
commit 7c26d8db4d
3 changed files with 255 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
/Cargo.lock

16
Cargo.toml Normal file
View file

@ -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 }

237
src/lib.rs Normal file
View file

@ -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<String>,
level: impl Into<tracing_subscriber::filter::LevelFilter>,
) -> Self {
Self {
inner: self.inner.with_target(target, level),
}
}
pub fn with_targets<T, L>(self, targets: impl IntoIterator<Item = (T, L)>) -> Self
where
String: From<T>,
tracing_subscriber::filter::LevelFilter: From<L>,
{
Self {
inner: self.inner.with_targets(targets),
}
}
pub fn with_default(self, level: impl Into<tracing_subscriber::filter::LevelFilter>) -> 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<T, L> Extend<(T, L)> for FmtTargets
where
T: Into<String>,
L: Into<tracing_subscriber::filter::LevelFilter>,
{
fn extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I) {
self.inner.extend(iter)
}
}
impl<T, L> FromIterator<(T, L)> for FmtTargets
where
T: Into<String>,
L: Into<tracing_subscriber::filter::LevelFilter>,
{
fn from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self {
Self {
inner: FromIterator::from_iter(iter),
}
}
}
impl IntoIterator for FmtTargets {
type Item = <tracing_subscriber::filter::Targets as IntoIterator>::Item;
type IntoIter = <tracing_subscriber::filter::Targets as IntoIterator>::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<S> tracing_subscriber::Layer<S> for FmtTargets
where
S: tracing_core::Subscriber,
{
fn enabled(
&self,
metadata: &tracing_core::Metadata<'_>,
ctx: tracing_subscriber::layer::Context<'_, S>,
) -> bool {
tracing_subscriber::Layer::<S>::enabled(&self.inner, metadata, ctx)
}
fn register_callsite(
&self,
metadata: &'static tracing_core::Metadata<'static>,
) -> tracing_core::subscriber::Interest {
tracing_subscriber::Layer::<S>::register_callsite(&self.inner, metadata)
}
fn max_level_hint(&self) -> Option<tracing_core::metadata::LevelFilter> {
tracing_subscriber::Layer::<S>::max_level_hint(&self.inner)
}
}
#[cfg(feature = "registry")]
impl<S> tracing_subscriber::layer::Filter<S> for FmtTargets {
fn enabled(
&self,
meta: &tracing_core::Metadata<'_>,
cx: &tracing_subscriber::layer::Context<'_, S>,
) -> bool {
tracing_subscriber::layer::Filter::<S>::enabled(&self.inner, meta, cx)
}
fn callsite_enabled(
&self,
meta: &'static tracing_core::Metadata<'static>,
) -> tracing_core::subscriber::Interest {
tracing_subscriber::layer::Filter::<S>::callsite_enabled(&self.inner, meta)
}
fn max_level_hint(&self) -> Option<tracing_subscriber::filter::LevelFilter> {
tracing_subscriber::layer::Filter::<S>::max_level_hint(&self.inner)
}
}
impl AsRef<tracing_subscriber::filter::Targets> for FmtTargets {
fn as_ref(&self) -> &tracing_subscriber::filter::Targets {
&self.inner
}
}
impl AsMut<tracing_subscriber::filter::Targets> for FmtTargets {
fn as_mut(&mut self) -> &mut tracing_subscriber::filter::Targets {
&mut self.inner
}
}
impl From<tracing_subscriber::filter::Targets> for FmtTargets {
fn from(inner: tracing_subscriber::filter::Targets) -> Self {
Self { inner }
}
}
impl From<FmtTargets> for tracing_subscriber::filter::Targets {
fn from(this: FmtTargets) -> Self {
this.inner
}
}
impl std::str::FromStr for FmtTargets {
type Err = <tracing_subscriber::filter::Targets as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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::<Vec<_>>()
.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");
}
}