use log::{Level, LevelFilter};
use super::FilterOp;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Directive {
pub kind: DirectiveKind,
pub level: LevelFilter,
pub negated: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DirectiveKind {
Any,
Module { module: String },
Component { component: String },
Topic { component: String, topic: FilterOp },
}
impl PartialOrd for DirectiveKind {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DirectiveKind {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
use core::cmp::Ordering;
match (self, other) {
(Self::Any, Self::Any) => Ordering::Equal,
(Self::Any, _) => Ordering::Less,
(_, Self::Any) => Ordering::Greater,
(Self::Module { module: l }, Self::Module { module: r }) => {
l.len().cmp(&r.len()).then_with(|| l.cmp(r))
}
(Self::Module { module: _ }, Self::Component { component: _ }) => Ordering::Greater,
(Self::Component { component: _ }, Self::Module { module: _ }) => Ordering::Less,
(Self::Module { module: _ }, _) => Ordering::Less,
(_, Self::Module { module: _ }) => Ordering::Greater,
(Self::Component { component: l }, Self::Component { component: r }) => {
l.len().cmp(&r.len()).then_with(|| l.cmp(r))
}
(Self::Component { component: _ }, _) => Ordering::Less,
(_, Self::Component { component: _ }) => Ordering::Greater,
(
Self::Topic {
component: lc,
topic: lt,
},
Self::Topic {
component: rc,
topic: rt,
},
) => lc.len().cmp(&rc.len()).then_with(|| lc.cmp(rc)).then(lt.cmp(rt)),
}
}
}
impl DirectiveKind {
pub fn matches(&self, target: &str) -> bool {
match self {
Self::Any => true,
Self::Module { module } => target.contains(module),
Self::Component { component } => {
let (target_component, _) = target.split_once(':').unwrap_or((target, ""));
component == target_component
}
Self::Topic { component, topic } => {
let (target_component, target_topic) =
target.split_once(':').unwrap_or((target, ""));
component == target_component && topic.is_match(target_topic)
}
}
}
}
pub fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
let mut was_matched = false;
for directive in directives.iter().rev() {
if was_matched && !directive.negated {
continue;
}
let (matches, negated) = match directive.level {
LevelFilter::Off => (directive.kind.matches(target), true),
filter if level <= filter => (directive.kind.matches(target), directive.negated),
_ => continue,
};
if matches && negated {
return false;
}
was_matched |= matches;
}
was_matched
}