diff options
Diffstat (limited to 'src/filters.rs')
-rw-r--r-- | src/filters.rs | 361 |
1 files changed, 0 insertions, 361 deletions
diff --git a/src/filters.rs b/src/filters.rs deleted file mode 100644 index 7da19ec..0000000 --- a/src/filters.rs +++ /dev/null @@ -1,361 +0,0 @@ -#![allow(clippy::new_ret_no_self)] -use std::collections::HashSet; -use std::ops; - -use evtclib::raw::parser::PartialEvtc; -use evtclib::statistics::gamedata::Boss; -use evtclib::{Agent, AgentName}; - -use num_derive::FromPrimitive; -use num_traits::FromPrimitive as _; - -use regex::Regex; - -use super::{guilds, FightOutcome, LogResult, SearchField, Weekday}; - -use chrono::{Datelike, NaiveDateTime}; - -/// 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)).unwrap() - } -} - -impl ops::BitAnd<Inclusion> for Inclusion { - type Output = Self; - - fn bitand(self, rhs: Inclusion) -> Self::Output { - Inclusion::from_i8((self as i8).min(rhs as i8)).unwrap() - } -} - -impl ops::BitOr<Inclusion> for Inclusion { - type Output = Self; - - fn bitor(self, rhs: Inclusion) -> Self::Output { - Inclusion::from_i8((self as i8).max(rhs as i8)).unwrap() - } -} - -impl From<bool> 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<dyn Filter>`. -pub trait Filter: Send + Sync { - /// Determine early (before processing all events) whether the log stands a chance to be - /// included. - /// - /// Note that you can return [Inclusion::Unkown] if this filter cannot determine yet a definite - /// answer. - fn filter_early(&self, _: &PartialEvtc) -> Inclusion { - Inclusion::Unknown - } - - /// Return whether the log should be included, according to this filter. - fn filter(&self, log: &LogResult) -> bool; -} - -#[derive(Debug, Clone, Copy)] -pub struct Const(pub bool); - -impl Const { - pub fn new(output: bool) -> Box<dyn Filter> { - Box::new(Const(output)) - } -} - -impl Filter for Const { - fn filter_early(&self, _: &PartialEvtc) -> Inclusion { - self.0.into() - } - - fn filter(&self, _: &LogResult) -> bool { - self.0 - } -} - -struct AndFilter(Box<dyn Filter>, Box<dyn Filter>); - -impl Filter for AndFilter { - fn filter_early(&self, partial_evtc: &PartialEvtc) -> 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: &LogResult) -> bool { - self.0.filter(log) && self.1.filter(log) - } -} - -impl ops::BitAnd<Box<dyn Filter>> for Box<dyn Filter> { - type Output = Box<dyn Filter>; - - fn bitand(self, rhs: Box<dyn Filter>) -> Self::Output { - Box::new(AndFilter(self, rhs)) - } -} - -struct OrFilter(Box<dyn Filter>, Box<dyn Filter>); - -impl Filter for OrFilter { - fn filter_early(&self, partial_evtc: &PartialEvtc) -> 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: &LogResult) -> bool { - self.0.filter(log) || self.1.filter(log) - } -} - -impl ops::BitOr<Box<dyn Filter>> for Box<dyn Filter> { - type Output = Box<dyn Filter>; - - fn bitor(self, rhs: Box<dyn Filter>) -> Self::Output { - Box::new(OrFilter(self, rhs)) - } -} - -struct NotFilter(Box<dyn Filter>); - -impl Filter for NotFilter { - fn filter_early(&self, partial_evtc: &PartialEvtc) -> Inclusion { - !self.0.filter_early(partial_evtc) - } - - fn filter(&self, log: &LogResult) -> bool { - !self.0.filter(log) - } -} - -impl ops::Not for Box<dyn Filter> { - type Output = Box<dyn Filter>; - - fn not(self) -> Self::Output { - Box::new(NotFilter(self)) - } -} - -// From here onwards, we have the specific filters - -/// Filter that filters according to the name. -/// -/// The given SearchField determines in which field something should be searched. -#[derive(Debug, Clone)] -pub struct NameFilter(SearchField, Regex); - -impl NameFilter { - pub fn new(field: SearchField, regex: Regex) -> Box<dyn Filter> { - Box::new(NameFilter(field, regex)) - } -} - -impl Filter for NameFilter { - fn filter_early(&self, partial_evtc: &PartialEvtc) -> Inclusion { - if self.0 == SearchField::Guild { - return Inclusion::Unknown; - } - - for player in &partial_evtc.agents { - let fancy = Agent::from_raw(player); - if let Ok(AgentName::Player { - ref account_name, - ref character_name, - .. - }) = fancy.as_ref().map(Agent::name) - { - let field = match self.0 { - SearchField::Account => account_name, - SearchField::Character => character_name, - _ => unreachable!("We already checked for Guild earlier"), - }; - if self.1.is_match(field) { - return Inclusion::Include; - } - } - } - Inclusion::Exclude - } - - fn filter(&self, log: &LogResult) -> bool { - for player in &log.players { - match self.0 { - SearchField::Account if self.1.is_match(&player.account_name) => return true, - SearchField::Character if self.1.is_match(&player.character_name) => return true, - SearchField::Guild => { - let guild_ok = player - .guild_id - .as_ref() - .and_then(|id| guilds::lookup(id)) - .map(|guild| self.1.is_match(guild.tag()) || self.1.is_match(guild.name())) - .unwrap_or(false); - if guild_ok { - return true; - } - } - _ => (), - } - } - false - } -} - -#[derive(Debug, Clone)] -pub struct BossFilter(HashSet<Boss>); - -impl BossFilter { - pub fn new(bosses: HashSet<Boss>) -> Box<dyn Filter> { - Box::new(BossFilter(bosses)) - } -} - -impl Filter for BossFilter { - fn filter_early(&self, partial_evtc: &PartialEvtc) -> Inclusion { - let boss = Boss::from_u16(partial_evtc.header.combat_id); - boss.map(|b| self.0.contains(&b).into()) - .unwrap_or(Inclusion::Include) - } - - fn filter(&self, log: &LogResult) -> bool { - let boss = Boss::from_u16(log.boss_id); - boss.map(|b| self.0.contains(&b)).unwrap_or(false) - } -} - -#[derive(Debug, Clone)] -pub struct OutcomeFilter(HashSet<FightOutcome>); - -impl OutcomeFilter { - pub fn new(outcomes: HashSet<FightOutcome>) -> Box<dyn Filter> { - Box::new(OutcomeFilter(outcomes)) - } -} - -impl Filter for OutcomeFilter { - fn filter(&self, log: &LogResult) -> bool { - self.0.contains(&log.outcome) - } -} - -#[derive(Debug, Clone)] -pub struct WeekdayFilter(HashSet<Weekday>); - -impl WeekdayFilter { - pub fn new(weekdays: HashSet<Weekday>) -> Box<dyn Filter> { - Box::new(WeekdayFilter(weekdays)) - } -} - -impl Filter for WeekdayFilter { - fn filter(&self, log: &LogResult) -> bool { - self.0.contains(&log.time.weekday()) - } -} - -#[derive(Debug, Clone)] -pub struct TimeFilter(Option<NaiveDateTime>, Option<NaiveDateTime>); - -impl TimeFilter { - pub fn new(after: Option<NaiveDateTime>, before: Option<NaiveDateTime>) -> Box<dyn Filter> { - Box::new(TimeFilter(after, before)) - } -} - -impl Filter for TimeFilter { - fn filter(&self, log: &LogResult) -> bool { - let after_ok = match self.0 { - Some(time) => time <= log.time, - None => true, - }; - let before_ok = match self.1 { - Some(time) => time >= log.time, - None => true, - }; - - after_ok && before_ok - } -} - -#[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); - } -} |