aboutsummaryrefslogtreecommitdiff
path: root/src/csl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/csl.rs')
-rw-r--r--src/csl.rs92
1 files changed, 92 insertions, 0 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)
+ }
+}