use serde::de::{self, Deserialize, Visitor};
use serde::ser::Serialize;
use std::fmt;
use std::str::FromStr;
#[derive(Copy, Clone, Debug, Deserialize, Display, EnumString, PartialEq, Serialize)]
pub enum Rule {
CombineRawAndWraps,
CombineSingleAndMulti,
CombineAllEquipment,
FourthAttemptsMayLower,
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct RuleSet(u32);
#[derive(Copy, Clone, Debug, Display, PartialEq)]
pub enum RuleSetParseError {
UnknownRule,
}
impl RuleSet {
pub fn contains(self, rule: Rule) -> bool {
self.0 & (1 << (rule as u32)) != 0
}
pub fn add(&mut self, rule: Rule) {
self.0 |= 1 << (rule as u32);
}
}
impl Serialize for RuleSet {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if self.0 == 0 {
serializer.serialize_str("")
} else {
serializer.serialize_u32(self.0)
}
}
}
impl FromStr for RuleSet {
type Err = RuleSetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(RuleSet::default());
}
if let Ok(n) = s.parse::<u32>() {
return Ok(RuleSet { 0: n });
}
let mut ruleset = RuleSet::default();
for substr in s.split(' ') {
if let Ok(rule) = substr.parse::<Rule>() {
ruleset.add(rule);
} else {
return Err(RuleSetParseError::UnknownRule);
}
}
Ok(ruleset)
}
}
struct RuleSetVisitor;
impl<'de> Visitor<'de> for RuleSetVisitor {
type Value = RuleSet;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a space-separated list of rules")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<RuleSet, E> {
RuleSet::from_str(value).map_err(E::custom)
}
}
impl<'de> Deserialize<'de> for RuleSet {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<RuleSet, D::Error> {
deserializer.deserialize_str(RuleSetVisitor)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rule_basic() {
let rule = "CombineRawAndWraps".parse::<Rule>().unwrap();
assert_eq!(rule, Rule::CombineRawAndWraps);
let rule = "CombineSingleAndMulti".parse::<Rule>().unwrap();
assert_eq!(rule, Rule::CombineSingleAndMulti);
}
#[test]
fn test_ruleset_basic() {
let ruleset = "CombineRawAndWraps".parse::<RuleSet>().unwrap();
assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), true);
assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), false);
let s = "CombineRawAndWraps CombineSingleAndMulti";
let ruleset = s.parse::<RuleSet>().unwrap();
assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), true);
assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), true);
let ruleset = "".parse::<RuleSet>().unwrap();
assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), false);
assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), false);
}
#[test]
fn test_ruleset_u32() {
let ruleset = "2".parse::<RuleSet>().unwrap();
assert_eq!(ruleset.contains(Rule::CombineRawAndWraps), false);
assert_eq!(ruleset.contains(Rule::CombineSingleAndMulti), true);
}
#[test]
fn test_ruleset_errors() {
let s = "CombineFloobAndBleeb";
assert!(s.parse::<RuleSet>().is_err());
let s = "CombineRawAndWraps CombineFloobAndBleeb";
assert!(s.parse::<RuleSet>().is_err());
let s = " CombineRawAndWraps";
assert!(s.parse::<RuleSet>().is_err());
let s = "-0";
assert!(s.parse::<RuleSet>().is_err());
}
}