From dfbf78622b0869f070d062b3edd40c4a97ce7dfd Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 16 Feb 2019 01:48:59 +0100 Subject: 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. --- src/csl.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/csl.rs (limited to 'src/csl.rs') 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; + 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 { + 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) + } +} -- cgit v1.2.3