use crate::Record;
use crate::RecordKind;
use itertools::Itertools;
use std::fmt;
pub trait RecordFilter: Send + 'static {
fn check(&self, record: &Record) -> bool;
fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("UnknownFilter")
}
}
impl<T: RecordFilter + ?Sized> RecordFilter for Box<T> {
fn check(&self, record: &Record) -> bool {
(**self).check(record)
}
fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt_debug(f)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DefaultFilter;
impl RecordFilter for DefaultFilter {
#[inline]
fn check(&self, _record: &Record) -> bool {
true
}
fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Debug)]
pub struct RecordKindFilter {
allowed_kinds: Vec<RecordKind>,
}
impl RecordKindFilter {
pub fn new(kinds: &'static [RecordKind]) -> Self {
Self {
allowed_kinds: kinds.iter().copied().unique().collect(),
}
}
}
impl RecordFilter for RecordKindFilter {
#[inline]
fn check(&self, record: &Record) -> bool {
self.allowed_kinds.contains(&record.kind)
}
fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
pub struct AllFilter {
filters: Vec<Box<dyn RecordFilter>>,
}
impl fmt::Debug for AllFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AllFilter")
.field("filters", &RecordFiltersDebug(&self.filters))
.finish()
}
}
impl AllFilter {
pub fn new(filters: Vec<Box<dyn RecordFilter>>) -> Self {
Self { filters }
}
}
impl RecordFilter for AllFilter {
#[inline]
fn check(&self, record: &Record) -> bool {
self.filters.iter().all(|filter| filter.check(record))
}
fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
pub struct AnyFilter {
filters: Vec<Box<dyn RecordFilter>>,
}
impl fmt::Debug for AnyFilter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AnyFilter")
.field("filters", &RecordFiltersDebug(&self.filters))
.finish()
}
}
impl AnyFilter {
pub fn new(filters: Vec<Box<dyn RecordFilter>>) -> Self {
Self { filters }
}
}
impl RecordFilter for AnyFilter {
#[inline]
fn check(&self, record: &Record) -> bool {
self.filters.iter().any(|filter| filter.check(record))
}
fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
struct RecordFilterDebug<'a>(&'a dyn RecordFilter);
impl fmt::Debug for RecordFilterDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt_debug(f)
}
}
struct RecordFiltersDebug<'a>(&'a [Box<dyn RecordFilter>]);
impl fmt::Debug for RecordFiltersDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut list = f.debug_list();
for filter in self.0.iter() {
list.entry(&RecordFilterDebug(filter.as_ref()));
}
list.finish()
}
}
#[cfg(test)]
mod tests {
use crate::filter::AllFilter;
use crate::filter::AnyFilter;
use crate::filter::DefaultFilter;
use crate::filter::RecordFilter;
use crate::filter::RecordKindFilter;
use crate::record::Record;
use crate::record::RecordKind;
use std::fmt;
fn assert_unpin<T: Unpin>() {}
#[test]
fn test_unpin() {
assert_unpin::<DefaultFilter>();
assert_unpin::<RecordKindFilter>();
assert_unpin::<AllFilter>();
assert_unpin::<AnyFilter>();
}
#[test]
fn test_default_filter() {
assert!(DefaultFilter.check(&Record::new(
RecordKind::Read,
String::from("01:02:03:04:05:06")
)));
assert!(DefaultFilter.check(&Record::new(
RecordKind::Write,
String::from("01:02:03:04:05:06")
)));
assert!(DefaultFilter.check(&Record::new(RecordKind::Drop, String::from("deallocated"))));
assert!(DefaultFilter.check(&Record::new(
RecordKind::Shutdown,
String::from("write shutdown request")
)));
}
#[test]
fn test_record_kind_filter() {
let filter = RecordKindFilter::new(&[RecordKind::Read]);
assert!(filter.check(&Record::new(
RecordKind::Read,
String::from("01:02:03:04:05:06")
)));
assert!(!filter.check(&Record::new(
RecordKind::Write,
String::from("01:02:03:04:05:06")
)));
assert!(!filter.check(&Record::new(RecordKind::Drop, String::from("deallocated"))));
assert!(!filter.check(&Record::new(
RecordKind::Shutdown,
String::from("write shutdown request")
)));
}
#[test]
fn test_all_filter_empty() {
let filter = AllFilter::new(vec![]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("test"))));
assert!(filter.check(&Record::new(RecordKind::Write, String::from("test"))));
assert!(filter.check(&Record::new(RecordKind::Error, String::from("test"))));
}
#[test]
fn test_all_filter_single() {
let filter = AllFilter::new(vec![Box::new(RecordKindFilter::new(&[RecordKind::Read]))]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Write, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("error"))));
}
#[test]
fn test_all_filter_multiple_all_pass() {
let filter = AllFilter::new(vec![
Box::new(RecordKindFilter::new(&[
RecordKind::Read,
RecordKind::Write,
])),
Box::new(RecordKindFilter::new(&[
RecordKind::Read,
RecordKind::Error,
])),
]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
}
#[test]
fn test_all_filter_multiple_one_fails() {
let filter = AllFilter::new(vec![
Box::new(RecordKindFilter::new(&[
RecordKind::Read,
RecordKind::Write,
])),
Box::new(RecordKindFilter::new(&[
RecordKind::Read,
RecordKind::Error,
])),
]);
assert!(!filter.check(&Record::new(RecordKind::Write, String::from("data"))));
}
#[test]
fn test_all_filter_multiple_all_fail() {
let filter = AllFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
]);
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("error"))));
}
#[test]
fn test_all_filter_with_default() {
let filter = AllFilter::new(vec![
Box::new(DefaultFilter),
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Write, String::from("data"))));
}
#[test]
fn test_any_filter_empty() {
let filter = AnyFilter::new(vec![]);
assert!(!filter.check(&Record::new(RecordKind::Read, String::from("test"))));
assert!(!filter.check(&Record::new(RecordKind::Write, String::from("test"))));
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("test"))));
}
#[test]
fn test_any_filter_single() {
let filter = AnyFilter::new(vec![Box::new(RecordKindFilter::new(&[RecordKind::Read]))]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Write, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("error"))));
}
#[test]
fn test_any_filter_multiple_first_passes() {
let filter = AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
}
#[test]
fn test_any_filter_multiple_second_passes() {
let filter = AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
]);
assert!(filter.check(&Record::new(RecordKind::Write, String::from("data"))));
}
#[test]
fn test_any_filter_multiple_all_pass() {
let filter = AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[
RecordKind::Read,
RecordKind::Write,
])),
Box::new(RecordKindFilter::new(&[
RecordKind::Read,
RecordKind::Error,
])),
]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
}
#[test]
fn test_any_filter_multiple_all_fail() {
let filter = AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
]);
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("error"))));
}
#[test]
fn test_any_filter_with_default() {
let filter = AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(DefaultFilter),
]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
assert!(filter.check(&Record::new(RecordKind::Write, String::from("data"))));
assert!(filter.check(&Record::new(RecordKind::Error, String::from("error"))));
}
#[test]
fn test_nested_composite_filters() {
let filter = AllFilter::new(vec![Box::new(AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
]))]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
assert!(filter.check(&Record::new(RecordKind::Write, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Drop, String::from("dropped"))));
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("error"))));
}
#[test]
fn test_complex_nested_filters() {
let filter = AllFilter::new(vec![
Box::new(AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
])),
Box::new(AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(RecordKindFilter::new(&[RecordKind::Error])),
])),
]);
assert!(filter.check(&Record::new(RecordKind::Read, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Write, String::from("data"))));
assert!(!filter.check(&Record::new(RecordKind::Error, String::from("error"))));
assert!(!filter.check(&Record::new(RecordKind::Drop, String::from("dropped"))));
}
#[test]
fn test_trait_object_safety() {
let default: Box<dyn RecordFilter> = Box::<DefaultFilter>::default();
let record_kind: Box<dyn RecordFilter> = Box::new(RecordKindFilter::new(&[]));
let all: Box<dyn RecordFilter> = Box::new(AllFilter::new(vec![]));
let any: Box<dyn RecordFilter> = Box::new(AnyFilter::new(vec![]));
let record = Record::new(RecordKind::Open, String::from("test log record"));
_ = default.check(&record);
_ = record_kind.check(&record);
_ = all.check(&record);
_ = any.check(&record);
}
fn assert_record_filter<T: RecordFilter>() {}
#[test]
fn test_box() {
assert_record_filter::<Box<dyn RecordFilter>>();
assert_record_filter::<Box<RecordKindFilter>>();
assert_record_filter::<Box<DefaultFilter>>();
assert_record_filter::<Box<AllFilter>>();
assert_record_filter::<Box<AnyFilter>>();
}
fn assert_send<T: Send>() {}
#[test]
fn test_send() {
assert_send::<RecordKindFilter>();
assert_send::<DefaultFilter>();
assert_send::<AllFilter>();
assert_send::<AnyFilter>();
assert_send::<Box<dyn RecordFilter>>();
assert_send::<Box<RecordKindFilter>>();
assert_send::<Box<DefaultFilter>>();
assert_send::<Box<AllFilter>>();
assert_send::<Box<AnyFilter>>();
}
fn assert_debug<T: fmt::Debug>() {}
#[test]
fn test_debug() {
assert_debug::<DefaultFilter>();
assert_debug::<RecordKindFilter>();
assert_debug::<AllFilter>();
assert_debug::<AnyFilter>();
assert_debug::<Box<DefaultFilter>>();
assert_debug::<Box<RecordKindFilter>>();
assert_debug::<Box<AllFilter>>();
assert_debug::<Box<AnyFilter>>();
}
#[test]
fn test_debug_output() {
assert_eq!(format!("{:?}", DefaultFilter), "DefaultFilter");
let record_kind_filter = RecordKindFilter::new(&[RecordKind::Read, RecordKind::Write]);
let debug_str = format!("{:?}", record_kind_filter);
assert!(debug_str.contains("RecordKindFilter"));
assert!(debug_str.contains("Read"));
assert!(debug_str.contains("Write"));
let all_filter_empty = AllFilter::new(vec![]);
let debug_str = format!("{:?}", all_filter_empty);
assert!(debug_str.contains("AllFilter"));
assert!(debug_str.contains("filters: []"));
let all_filter = AllFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Read])),
Box::new(DefaultFilter),
]);
let debug_str = format!("{:?}", all_filter);
assert!(debug_str.contains("AllFilter"));
assert!(debug_str.contains("RecordKindFilter"));
assert!(debug_str.contains("Read"));
assert!(debug_str.contains("DefaultFilter"));
let any_filter_empty = AnyFilter::new(vec![]);
let debug_str = format!("{:?}", any_filter_empty);
assert!(debug_str.contains("AnyFilter"));
assert!(debug_str.contains("filters: []"));
let any_filter = AnyFilter::new(vec![
Box::new(RecordKindFilter::new(&[RecordKind::Write])),
Box::new(DefaultFilter),
]);
let debug_str = format!("{:?}", any_filter);
assert!(debug_str.contains("AnyFilter"));
assert!(debug_str.contains("RecordKindFilter"));
assert!(debug_str.contains("Write"));
assert!(debug_str.contains("DefaultFilter"));
let nested = AllFilter::new(vec![Box::new(AnyFilter::new(vec![Box::new(
RecordKindFilter::new(&[RecordKind::Read]),
)]))]);
let debug_str = format!("{:?}", nested);
assert!(debug_str.contains("AllFilter"));
assert!(debug_str.contains("AnyFilter"));
assert!(debug_str.contains("RecordKindFilter"));
assert!(debug_str.contains("Read"));
}
}