use std::{fmt, ops}; use num_derive::FromPrimitive; use num_traits::FromPrimitive as _; pub mod log; pub mod player; pub mod values; /// Early filtering result. /// /// This implements a [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), /// similar to SQL's `NULL`. #[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)] #[repr(i8)] pub enum Inclusion { /// The log should be included. Include = 1, /// The state is yet unknown. Unknown = 0, /// The log should be excluded. Exclude = -1, } impl ops::Not for Inclusion { type Output = Self; fn not(self) -> Self::Output { Inclusion::from_i8(-(self as i8)).expect("i8 is from pre-existing Inclusion") } } impl ops::BitAnd for Inclusion { type Output = Self; fn bitand(self, rhs: Inclusion) -> Self::Output { Inclusion::from_i8((self as i8).min(rhs as i8)).expect("i8 is from pre-existing Inclusion") } } impl ops::BitOr for Inclusion { type Output = Self; fn bitor(self, rhs: Inclusion) -> Self::Output { Inclusion::from_i8((self as i8).max(rhs as i8)).expect("i8 is from pre-existing Inclusion") } } impl From for Inclusion { fn from(data: bool) -> Self { if data { Inclusion::Include } else { Inclusion::Exclude } } } /// The main filter trait. /// /// Filters are usually handled as a `Box`. pub trait Filter: Send + Sync + fmt::Debug { /// Determine early (before processing all events) whether the log stands a chance to be /// included. /// /// Note that you can return [Inclusion::Unknown] if this filter cannot determine yet a /// definite answer. fn filter_early(&self, _: &Early) -> Inclusion { Inclusion::Unknown } /// Return whether the log should be included, according to this filter. fn filter(&self, _: &Late) -> bool; } #[derive(Debug, Clone, Copy)] struct Const(pub bool); impl Filter for Const { fn filter_early(&self, _: &E) -> Inclusion { self.0.into() } fn filter(&self, _: &L) -> bool { self.0 } } /// Construct a `Filter` that always returns a fixed value. pub fn constant(output: bool) -> Box> { Box::new(Const(output)) } struct AndFilter(Box>, Box>); impl Filter for AndFilter { fn filter_early(&self, partial_evtc: &E) -> Inclusion { let lhs = self.0.filter_early(partial_evtc); // Short circuit behaviour if lhs == Inclusion::Exclude { Inclusion::Exclude } else { lhs & self.1.filter_early(partial_evtc) } } fn filter(&self, log: &L) -> bool { self.0.filter(log) && self.1.filter(log) } } impl ops::BitAnd>> for Box> { type Output = Box>; fn bitand(self, rhs: Box>) -> Self::Output { Box::new(AndFilter(self, rhs)) } } impl fmt::Debug for AndFilter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({:?}) and ({:?})", self.0, self.1) } } struct OrFilter(Box>, Box>); impl Filter for OrFilter { fn filter_early(&self, partial_evtc: &E) -> Inclusion { let lhs = self.0.filter_early(partial_evtc); // Short circuit behaviour if lhs == Inclusion::Include { Inclusion::Include } else { lhs | self.1.filter_early(partial_evtc) } } fn filter(&self, log: &L) -> bool { self.0.filter(log) || self.1.filter(log) } } impl ops::BitOr>> for Box> { type Output = Box>; fn bitor(self, rhs: Box>) -> Self::Output { Box::new(OrFilter(self, rhs)) } } impl fmt::Debug for OrFilter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({:?}) or ({:?})", self.0, self.1) } } struct NotFilter(Box>); impl Filter for NotFilter { fn filter_early(&self, partial_evtc: &E) -> Inclusion { !self.0.filter_early(partial_evtc) } fn filter(&self, log: &L) -> bool { !self.0.filter(log) } } impl ops::Not for Box> { type Output = Box>; fn not(self) -> Self::Output { Box::new(NotFilter(self)) } } impl fmt::Debug for NotFilter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "not ({:?})", self.0) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_inclusion_not() { use Inclusion::*; assert_eq!(!Exclude, Include); assert_eq!(!Include, Exclude); assert_eq!(!Unknown, Unknown); } #[test] fn test_inclusion_and() { use Inclusion::*; assert_eq!(Exclude & Exclude, Exclude); assert_eq!(Exclude & Unknown, Exclude); assert_eq!(Exclude & Include, Exclude); assert_eq!(Unknown & Exclude, Exclude); assert_eq!(Unknown & Unknown, Unknown); assert_eq!(Unknown & Include, Unknown); assert_eq!(Include & Exclude, Exclude); assert_eq!(Include & Unknown, Unknown); assert_eq!(Include & Include, Include); } #[test] fn test_inclusion_or() { use Inclusion::*; assert_eq!(Exclude | Exclude, Exclude); assert_eq!(Exclude | Unknown, Unknown); assert_eq!(Exclude | Include, Include); assert_eq!(Unknown | Exclude, Unknown); assert_eq!(Unknown | Unknown, Unknown); assert_eq!(Unknown | Include, Include); assert_eq!(Include | Exclude, Include); assert_eq!(Include | Unknown, Include); assert_eq!(Include | Include, Include); } }