1use serde::de::{self, Deserialize, Visitor};
4use serde::ser::Serialize;
5
6use std::fmt;
7use std::str::FromStr;
8
9#[derive(Copy, Clone, Debug, Deserialize, Display, EnumString, PartialEq, Serialize)]
13pub enum Rule {
14 CombineRawAndWraps,
16
17 CombineSingleAndMulti,
19
20 CombineAllEquipment,
22
23 FourthAttemptsMayLower,
25}
26
27#[derive(Copy, Clone, Debug, Default, PartialEq)]
42pub struct RuleSet(u32);
43
44#[derive(Copy, Clone, Debug, Display, PartialEq)]
45pub enum RuleSetParseError {
46 UnknownRule,
47}
48
49impl RuleSet {
50 pub fn contains(self, rule: Rule) -> bool {
61 self.0 & (1 << (rule as u32)) != 0
62 }
63
64 pub fn add(&mut self, rule: Rule) {
76 self.0 |= 1 << (rule as u32);
77 }
78}
79
80impl Serialize for RuleSet {
81 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
82 if self.0 == 0 {
83 serializer.serialize_str("")
85 } else {
86 serializer.serialize_u32(self.0)
87 }
88 }
89}
90
91impl FromStr for RuleSet {
92 type Err = RuleSetParseError;
93
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 if s.is_empty() {
97 return Ok(RuleSet::default());
98 }
99
100 if let Ok(n) = s.parse::<u32>() {
102 return Ok(RuleSet { 0: n });
103 }
104
105 let mut ruleset = RuleSet::default();
107 for substr in s.split(' ') {
108 if let Ok(rule) = substr.parse::<Rule>() {
109 ruleset.add(rule);
110 } else {
111 return Err(RuleSetParseError::UnknownRule);
112 }
113 }
114 Ok(ruleset)
115 }
116}
117
118struct RuleSetVisitor;
119
120impl<'de> Visitor<'de> for RuleSetVisitor {
121 type Value = RuleSet;
122
123 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
124 formatter.write_str("a space-separated list of rules")
125 }
126
127 fn visit_str<E: de::Error>(self, value: &str) -> Result<RuleSet, E> {
128 RuleSet::from_str(value).map_err(E::custom)
129 }
130}
131
132impl<'de> Deserialize<'de> for RuleSet {
133 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<RuleSet, D::Error> {
134 deserializer.deserialize_str(RuleSetVisitor)
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use super::*;
141
142 #[test]
143 fn test_rule_basic() {
144 let rule = "CombineRawAndWraps".parse::<Rule>().unwrap();
145 assert_eq!(rule, Rule::CombineRawAndWraps);
146 let rule = "CombineSingleAndMulti".parse::<Rule>().unwrap();
147 assert_eq!(rule, Rule::CombineSingleAndMulti);
148 }
149
150 #[test]
151 fn test_ruleset_basic() {
152 let ruleset = "CombineRawAndWraps".parse::<RuleSet>().unwrap();
153 assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), true);
154 assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), false);
155
156 let s = "CombineRawAndWraps CombineSingleAndMulti";
157 let ruleset = s.parse::<RuleSet>().unwrap();
158 assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), true);
159 assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), true);
160
161 let ruleset = "".parse::<RuleSet>().unwrap();
162 assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), false);
163 assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), false);
164 }
165
166 #[test]
168 fn test_ruleset_u32() {
169 let ruleset = "2".parse::<RuleSet>().unwrap();
170 assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), false);
171 assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), true);
172 }
173
174 #[test]
175 fn test_ruleset_errors() {
176 let s = "CombineFloobAndBleeb";
177 assert!(s.parse::<RuleSet>().is_err());
178
179 let s = "CombineRawAndWraps CombineFloobAndBleeb";
180 assert!(s.parse::<RuleSet>().is_err());
181
182 let s = " CombineRawAndWraps";
183 assert!(s.parse::<RuleSet>().is_err());
184
185 let s = "-0";
186 assert!(s.parse::<RuleSet>().is_err());
187 }
188}