diff options
author | Daniel <kingdread@gmx.de> | 2019-02-16 01:48:59 +0100 |
---|---|---|
committer | Daniel <kingdread@gmx.de> | 2019-02-16 01:48:59 +0100 |
commit | dfbf78622b0869f070d062b3edd40c4a97ce7dfd (patch) | |
tree | 980e0fc50f5b0e89210a0d086fb9ee87c1e73f51 /src | |
parent | 2fb14e9e5c5e48c3231f6d4aa324917d0dd3ccf7 (diff) | |
download | raidgrep-dfbf78622b0869f070d062b3edd40c4a97ce7dfd.tar.gz raidgrep-dfbf78622b0869f070d062b3edd40c4a97ce7dfd.tar.bz2 raidgrep-dfbf78622b0869f070d062b3edd40c4a97ce7dfd.zip |
introduce CommaSeparatedList
This gives a common interface for command line flags which take multiple
values, possibly with negation.
This might come in useful if we add filtering by boss, e.g. "--boss
!deimos" to ignore all deimos logs.
Diffstat (limited to 'src')
-rw-r--r-- | src/csl.rs | 92 | ||||
-rw-r--r-- | src/filters.rs | 12 | ||||
-rw-r--r-- | src/main.rs | 34 |
3 files changed, 106 insertions, 32 deletions
diff --git a/src/csl.rs b/src/csl.rs new file mode 100644 index 0000000..83f2e14 --- /dev/null +++ b/src/csl.rs @@ -0,0 +1,92 @@ +use std::collections::HashSet; +use std::hash::Hash; +use std::str::FromStr; +use std::fmt; + +use super::{SearchField, FightOutcome}; + +pub trait Variants: Copy { + type Output: Iterator<Item=Self>; + 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() + } + } + } +} + +variants! { SearchField => Account, Character } +variants! { FightOutcome => Success, Wipe } + +/// 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<T: Eq + Hash + fmt::Debug> { + values: HashSet<T>, +} + +#[derive(Debug, Clone)] +pub enum ParseError<E> { + Underlying(E), +} + +impl<E: fmt::Display> fmt::Display for ParseError<E> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ParseError::Underlying(ref e) => e.fmt(f), + } + } +} + +impl<T> FromStr for CommaSeparatedList<T> + where T: FromStr + Variants + Hash + Eq + fmt::Debug +{ + type Err = ParseError<T::Err>; + + fn from_str(input: &str) -> Result<Self, Self::Err> { + 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::<HashSet<_>>(); + 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::<Result<HashSet<_>, _>>() + .map_err(ParseError::Underlying)?; + Ok(CommaSeparatedList { values }) + } + } +} + +impl<T> CommaSeparatedList<T> + where T: Hash + Eq + fmt::Debug +{ + pub fn contains(&self, value: &T) -> bool { + self.values.contains(value) + } +} diff --git a/src/filters.rs b/src/filters.rs index 6980900..f9db377 100644 --- a/src/filters.rs +++ b/src/filters.rs @@ -1,6 +1,6 @@ use evtclib::{AgentName, Log}; -use super::{LogResult, Opt}; +use super::{SearchField, FightOutcome, LogResult, Opt}; /// Do filtering based on the character or account name. pub fn filter_name(log: &Log, opt: &Opt) -> bool { @@ -11,8 +11,8 @@ pub fn filter_name(log: &Log, opt: &Opt) -> bool { character_name, .. } => { - if (opt.field.search_account() && opt.expression.is_match(account_name)) - || (opt.field.search_character() && opt.expression.is_match(character_name)) + if (opt.field.contains(&SearchField::Account) && opt.expression.is_match(account_name)) + || (opt.field.contains(&SearchField::Character) && opt.expression.is_match(character_name)) { return true; } @@ -25,11 +25,7 @@ pub fn filter_name(log: &Log, opt: &Opt) -> bool { /// Do filtering based on the fight outcome. pub fn filter_outcome(result: &LogResult, opt: &Opt) -> bool { - match opt.outcome { - Some(o) if o == result.outcome => true, - None => true, - _ => false, - } + opt.outcome.contains(&result.outcome) } /// Do filtering based on encounter time. diff --git a/src/main.rs b/src/main.rs index a77a952..13d0a76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,9 @@ mod output; mod filters; +mod csl; +use csl::CommaSeparatedList; + macro_rules! unwrap { ($p:pat = $e:expr => { $r:expr} ) => { if let $p = $e { @@ -75,12 +78,12 @@ pub struct Opt { path: PathBuf, /// The fields which should be searched. - #[structopt(short = "f", long = "fields", default_value = "all")] - field: SearchField, + #[structopt(short = "f", long = "fields", default_value = "*")] + field: CommaSeparatedList<SearchField>, /// Only display fights with the given outcome. - #[structopt(short = "o", long = "outcome")] - outcome: Option<FightOutcome>, + #[structopt(short = "o", long = "outcome", default_value = "*")] + outcome: CommaSeparatedList<FightOutcome>, /// Disable colored output. #[structopt(long = "no-color")] @@ -112,39 +115,22 @@ pub struct Opt { } /// A flag indicating which fields should be searched. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] enum SearchField { - /// Search all fields. - All, /// Only search the account name. Account, /// Only search the character name. Character, } -impl SearchField { - /// True if the state says that the account name should be searched. - #[inline] - fn search_account(self) -> bool { - self == SearchField::All || self == SearchField::Account - } - - /// True if the state says that the character name should be searched. - #[inline] - fn search_character(self) -> bool { - self == SearchField::All || self == SearchField::Character - } -} - impl FromStr for SearchField { type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { - "all" => Ok(SearchField::All), "account" => Ok(SearchField::Account), "character" => Ok(SearchField::Character), - _ => Err("Must be all, account or character"), + _ => Err("Must be account or character"), } } } @@ -178,7 +164,7 @@ pub struct Player { } /// Outcome of the fight. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum FightOutcome { Success, Wipe, |