From 7881ba85ff40f3d22237ef903c7241b56aa9c185 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 15 Apr 2020 15:01:10 +0200 Subject: new filter pipeline This is the groundwork for introducing more complex filter queries like `find` has. Filters can be arbitrarily combined with and/or/not and support an "early filter" mode. So far, the filters have been translated pretty mechanically to match the current command line arguments, so now new syntax has been introduced. The NameFilter is not yet in its final version. The goal is to support something like PlayerAll/PlayerExists and have a PlayerFilter that works on single players instead of the complete log, but that might introduce some code duplication as we then need a PlayerFilterAnd, PlayerFilterOr, ... Some digging has to be done into whether we can reduce that duplication without devolving into madness or resorting to macros. Maybe some type-level generic hackery could be done? Maybe an enum instead of dynamic traits should be used, at least for the base functions? --- src/csl.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'src/csl.rs') diff --git a/src/csl.rs b/src/csl.rs index e7d84f3..ac20ada 100644 --- a/src/csl.rs +++ b/src/csl.rs @@ -1,14 +1,14 @@ use std::collections::HashSet; +use std::fmt; use std::hash::Hash; use std::str::FromStr; -use std::fmt; -use super::{SearchField, FightOutcome}; +use super::{FightOutcome, SearchField}; use chrono::Weekday; use evtclib::statistics::gamedata::Boss; pub trait Variants: Copy { - type Output: Iterator; + type Output: Iterator; fn variants() -> Self::Output; } @@ -79,18 +79,21 @@ impl fmt::Display for ParseError { } impl FromStr for CommaSeparatedList - where T: FromStr + Variants + Hash + Eq + fmt::Debug +where + T: FromStr + Variants + Hash + Eq + fmt::Debug, { type Err = ParseError; fn from_str(input: &str) -> Result { if input == "*" { - Ok(CommaSeparatedList { values: T::variants().collect() }) + Ok(CommaSeparatedList { + values: T::variants().collect(), + }) } else if input.starts_with(NEGATOR) { let no_csl = CommaSeparatedList::from_str(&input[1..])?; let all_values = T::variants().collect::>(); Ok(CommaSeparatedList { - values: all_values.difference(&no_csl.values).cloned().collect() + values: all_values.difference(&no_csl.values).cloned().collect(), }) } else { let parts = input.split(DELIMITER); @@ -104,9 +107,26 @@ impl FromStr for CommaSeparatedList } impl CommaSeparatedList - where T: Hash + Eq + fmt::Debug +where + T: Hash + Eq + fmt::Debug, { pub fn contains(&self, value: &T) -> bool { self.values.contains(value) } + + pub fn values(&self) -> &HashSet { + &self.values + } +} + +// We allow implicit hasher because then it's a zero-cost conversion, as we're just unwrapping the +// values. +#[allow(clippy::implicit_hasher)] +impl From> for HashSet +where + T: Hash + Eq + fmt::Debug, +{ + fn from(csl: CommaSeparatedList) -> Self { + csl.values + } } -- cgit v1.2.3 From 0e4e148a0890ba206df40cffe5a5f1cc47c8079e Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 20 Apr 2020 14:27:42 +0200 Subject: hook up new expression parser to command line args This method is not perfect yet, because 1. The items are not documented as they were before 2. You need to separate the args with --, otherwise Clap tries to parse them as optional flags This should be fixed (especially the documentation part) before merging into master. --- src/csl.rs | 132 ------------------------------------------------------------- 1 file changed, 132 deletions(-) delete mode 100644 src/csl.rs (limited to 'src/csl.rs') diff --git a/src/csl.rs b/src/csl.rs deleted file mode 100644 index ac20ada..0000000 --- a/src/csl.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::collections::HashSet; -use std::fmt; -use std::hash::Hash; -use std::str::FromStr; - -use super::{FightOutcome, SearchField}; -use chrono::Weekday; -use evtclib::statistics::gamedata::Boss; - -pub trait Variants: Copy { - type Output: Iterator; - fn variants() -> Self::Output; -} - -macro_rules! variants { - ($target:ident => $($var:ident),+) => { - impl Variants for $target { - type Output = ::std::iter::Cloned<::std::slice::Iter<'static, Self>>; - fn variants() -> Self::Output { - // Exhaustiveness check - #[allow(dead_code)] - fn exhaustiveness_check(value: $target) { - match value { - $($target :: $var => ()),+ - } - } - // Actual result - [ - $($target :: $var),+ - ].iter().cloned() - } - } - }; - ($target:ident => $($var:ident,)+) => { - variants!($target => $($var),+); - }; -} - -variants! { SearchField => Account, Character, Guild } -variants! { FightOutcome => Success, Wipe } -variants! { Weekday => Mon, Tue, Wed, Thu, Fri, Sat, Sun } -variants! { Boss => - ValeGuardian, Gorseval, Sabetha, - Slothasor, Matthias, - KeepConstruct, Xera, - Cairn, MursaatOverseer, Samarog, Deimos, - SoullessHorror, Dhuum, - ConjuredAmalgamate, LargosTwins, Qadim, - CardinalAdina, CardinalSabir, QadimThePeerless, - - IcebroodConstruct, VoiceOfTheFallen, FraenirOfJormag, Boneskinner, WhisperOfJormag, - - Skorvald, Artsariiv, Arkk, - MAMA, Siax, Ensolyss, -} - -/// The character that delimits items from each other. -const DELIMITER: char = ','; -/// The character that negates the result. -const NEGATOR: char = '!'; - -/// A list that is given as comma-separated values. -#[derive(Debug, Clone)] -pub struct CommaSeparatedList { - values: HashSet, -} - -#[derive(Debug, Clone)] -pub enum ParseError { - Underlying(E), -} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ParseError::Underlying(ref e) => e.fmt(f), - } - } -} - -impl FromStr for CommaSeparatedList -where - T: FromStr + Variants + Hash + Eq + fmt::Debug, -{ - type Err = ParseError; - - fn from_str(input: &str) -> Result { - if input == "*" { - Ok(CommaSeparatedList { - values: T::variants().collect(), - }) - } else if input.starts_with(NEGATOR) { - let no_csl = CommaSeparatedList::from_str(&input[1..])?; - let all_values = T::variants().collect::>(); - Ok(CommaSeparatedList { - values: all_values.difference(&no_csl.values).cloned().collect(), - }) - } else { - let parts = input.split(DELIMITER); - let values = parts - .map(FromStr::from_str) - .collect::, _>>() - .map_err(ParseError::Underlying)?; - Ok(CommaSeparatedList { values }) - } - } -} - -impl CommaSeparatedList -where - T: Hash + Eq + fmt::Debug, -{ - pub fn contains(&self, value: &T) -> bool { - self.values.contains(value) - } - - pub fn values(&self) -> &HashSet { - &self.values - } -} - -// We allow implicit hasher because then it's a zero-cost conversion, as we're just unwrapping the -// values. -#[allow(clippy::implicit_hasher)] -impl From> for HashSet -where - T: Hash + Eq + fmt::Debug, -{ - fn from(csl: CommaSeparatedList) -> Self { - csl.values - } -} -- cgit v1.2.3