use crate::{
filter::{
directive::{DirectiveSet, ParseError, StaticDirective},
LevelFilter,
},
layer,
};
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::{
iter::{Extend, FilterMap, FromIterator},
slice,
str::FromStr,
};
use tracing_core::{Interest, Level, Metadata, Subscriber};
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Targets(DirectiveSet<StaticDirective>);
impl Targets {
pub fn new() -> Self {
Self::default()
}
pub fn with_target(mut self, target: impl Into<String>, level: impl Into<LevelFilter>) -> Self {
self.0.add(StaticDirective::new(
Some(target.into()),
Default::default(),
level.into(),
));
self
}
pub fn with_targets<T, L>(mut self, targets: impl IntoIterator<Item = (T, L)>) -> Self
where
String: From<T>,
LevelFilter: From<L>,
{
self.extend(targets);
self
}
pub fn with_default(mut self, level: impl Into<LevelFilter>) -> Self {
self.0
.add(StaticDirective::new(None, Default::default(), level.into()));
self
}
pub fn default_level(&self) -> Option<LevelFilter> {
self.0.directives().into_iter().find_map(|d| {
if d.target.is_none() {
Some(d.level)
} else {
None
}
})
}
pub fn iter(&self) -> Iter<'_> {
self.into_iter()
}
#[inline]
fn interested(&self, metadata: &'static Metadata<'static>) -> Interest {
if self.0.enabled(metadata) {
Interest::always()
} else {
Interest::never()
}
}
pub fn would_enable(&self, target: &str, level: &Level) -> bool {
self.0.target_enabled(target, level)
}
}
impl<T, L> Extend<(T, L)> for Targets
where
T: Into<String>,
L: Into<LevelFilter>,
{
fn extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I) {
let iter = iter.into_iter().map(|(target, level)| {
StaticDirective::new(Some(target.into()), Default::default(), level.into())
});
self.0.extend(iter);
}
}
impl<T, L> FromIterator<(T, L)> for Targets
where
T: Into<String>,
L: Into<LevelFilter>,
{
fn from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self {
let mut this = Self::default();
this.extend(iter);
this
}
}
impl FromStr for Targets {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.split(',')
.map(StaticDirective::from_str)
.collect::<Result<_, _>>()
.map(Self)
}
}
impl<S> layer::Layer<S> for Targets
where
S: Subscriber,
{
fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool {
self.0.enabled(metadata)
}
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.interested(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(self.0.max_level)
}
}
#[cfg(feature = "registry")]
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
impl<S> layer::Filter<S> for Targets {
fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool {
self.0.enabled(metadata)
}
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
self.interested(metadata)
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(self.0.max_level)
}
}
impl IntoIterator for Targets {
type Item = (String, LevelFilter);
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
impl<'a> IntoIterator for &'a Targets {
type Item = (&'a str, LevelFilter);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
Iter::new(self)
}
}
#[derive(Debug)]
pub struct IntoIter(
#[allow(clippy::type_complexity)] FilterMap<
<DirectiveSet<StaticDirective> as IntoIterator>::IntoIter,
fn(StaticDirective) -> Option<(String, LevelFilter)>,
>,
);
impl IntoIter {
fn new(targets: Targets) -> Self {
Self(targets.0.into_iter().filter_map(|directive| {
let level = directive.level;
directive.target.map(|target| (target, level))
}))
}
}
impl Iterator for IntoIter {
type Item = (String, LevelFilter);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[derive(Debug)]
pub struct Iter<'a>(
FilterMap<
slice::Iter<'a, StaticDirective>,
fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>,
>,
);
impl<'a> Iter<'a> {
fn new(targets: &'a Targets) -> Self {
Self(targets.0.iter().filter_map(|directive| {
directive
.target
.as_deref()
.map(|target| (target, directive.level))
}))
}
}
impl<'a> Iterator for Iter<'a> {
type Item = (&'a str, LevelFilter);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[cfg(test)]
mod tests {
use super::*;
feature! {
#![not(feature = "std")]
use alloc::{vec, vec::Vec, string::ToString};
macro_rules! dbg {
($x:expr) => { $x }
}
}
fn expect_parse(s: &str) -> Targets {
match dbg!(s).parse::<Targets>() {
Err(e) => panic!("string {:?} did not parse successfully: {}", s, e),
Ok(e) => e,
}
}
fn expect_parse_ralith(s: &str) {
let dirs = expect_parse(s).0.into_vec();
assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("server".to_string()));
assert_eq!(dirs[0].level, LevelFilter::DEBUG);
assert_eq!(dirs[0].field_names, Vec::<String>::new());
assert_eq!(dirs[1].target, Some("common".to_string()));
assert_eq!(dirs[1].level, LevelFilter::INFO);
assert_eq!(dirs[1].field_names, Vec::<String>::new());
}
fn expect_parse_level_directives(s: &str) {
let dirs = expect_parse(s).0.into_vec();
assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string()));
assert_eq!(dirs[0].level, LevelFilter::OFF);
assert_eq!(dirs[0].field_names, Vec::<String>::new());
assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string()));
assert_eq!(dirs[1].level, LevelFilter::INFO);
assert_eq!(dirs[1].field_names, Vec::<String>::new());
assert_eq!(dirs[2].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[2].level, LevelFilter::WARN);
assert_eq!(dirs[2].field_names, Vec::<String>::new());
assert_eq!(dirs[3].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[3].level, LevelFilter::ERROR);
assert_eq!(dirs[3].field_names, Vec::<String>::new());
assert_eq!(dirs[4].target, Some("crate3".to_string()));
assert_eq!(dirs[4].level, LevelFilter::TRACE);
assert_eq!(dirs[4].field_names, Vec::<String>::new());
assert_eq!(dirs[5].target, Some("crate2".to_string()));
assert_eq!(dirs[5].level, LevelFilter::DEBUG);
assert_eq!(dirs[5].field_names, Vec::<String>::new());
}
#[test]
fn parse_ralith() {
expect_parse_ralith("common=info,server=debug");
}
#[test]
fn parse_ralith_uc() {
expect_parse_ralith("common=INFO,server=DEBUG");
}
#[test]
fn parse_ralith_mixed() {
expect_parse("common=iNfo,server=dEbUg");
}
#[test]
fn expect_parse_valid() {
let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.0
.into_vec();
assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
assert_eq!(dirs[0].target, Some("crate1::mod2".to_string()));
assert_eq!(dirs[0].level, LevelFilter::TRACE);
assert_eq!(dirs[0].field_names, Vec::<String>::new());
assert_eq!(dirs[1].target, Some("crate1::mod1".to_string()));
assert_eq!(dirs[1].level, LevelFilter::ERROR);
assert_eq!(dirs[1].field_names, Vec::<String>::new());
assert_eq!(dirs[2].target, Some("crate3".to_string()));
assert_eq!(dirs[2].level, LevelFilter::OFF);
assert_eq!(dirs[2].field_names, Vec::<String>::new());
assert_eq!(dirs[3].target, Some("crate2".to_string()));
assert_eq!(dirs[3].level, LevelFilter::DEBUG);
assert_eq!(dirs[3].field_names, Vec::<String>::new());
}
#[test]
fn parse_level_directives() {
expect_parse_level_directives(
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
)
}
#[test]
fn parse_uppercase_level_directives() {
expect_parse_level_directives(
"crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
)
}
#[test]
fn parse_numeric_level_directives() {
expect_parse_level_directives(
"crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
crate3=5,crate3::mod2::mod1=0",
)
}
#[test]
fn targets_iter() {
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::WARN);
let mut targets: Vec<_> = filter.iter().collect();
targets.sort();
assert_eq!(
targets,
vec![
("crate1::mod1", LevelFilter::ERROR),
("crate1::mod2", LevelFilter::TRACE),
("crate2", LevelFilter::DEBUG),
("crate3", LevelFilter::OFF),
]
);
}
#[test]
fn targets_into_iter() {
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::WARN);
let mut targets: Vec<_> = filter.into_iter().collect();
targets.sort();
assert_eq!(
targets,
vec![
("crate1::mod1".to_string(), LevelFilter::ERROR),
("crate1::mod2".to_string(), LevelFilter::TRACE),
("crate2".to_string(), LevelFilter::DEBUG),
("crate3".to_string(), LevelFilter::OFF),
]
);
}
#[test]
fn targets_default_level() {
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
assert_eq!(filter.default_level(), None);
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::OFF);
assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
.with_default(LevelFilter::OFF)
.with_default(LevelFilter::INFO);
assert_eq!(filter.default_level(), Some(LevelFilter::INFO));
}
#[test]
#[cfg(feature = "std")]
fn size_of_filters() {
fn print_sz(s: &str) {
let filter = s.parse::<Targets>().expect("filter should parse");
println!(
"size_of_val({:?})\n -> {}B",
s,
std::mem::size_of_val(&filter)
);
}
print_sz("info");
print_sz("foo=debug");
print_sz(
"crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
crate2=debug,crate3=trace,crate3::mod2::mod1=off",
);
}
}