1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
use std::collections::HashSet;
use std::hash::Hash;
use std::str::FromStr;
use std::fmt;
use super::{SearchField, FightOutcome};
use chrono::Weekday;
use evtclib::statistics::gamedata::Boss;
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()
}
}
};
($target:ident => $($var:ident,)+) => {
variants!($target => $($var),+);
};
}
variants! { SearchField => Account, Character }
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,
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<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)
}
}
|