diff options
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, | 
