use std::ops::{BitAnd, BitOr, Not};
pub trait Checkable {
fn check_that(&self, condition: Condition) -> bool;
}
#[derive(Clone)]
pub enum Condition {
IsInt,
IsFloat,
IsStr,
IsList,
IsBool,
IsMap,
And(Box<Condition>, Box<Condition>),
Or(Box<Condition>, Box<Condition>),
Not(Box<Condition>),
IsExactlyInt(super::_Int),
IsExactlyFloat(super::_Float),
IsExactlyStr(super::_Str),
IsExactlyList(Vec<super::CfgValue>),
IsExactlyMap(super::CfgMap),
IsTrue,
IsListWith(Box<Condition>),
IsListWithLength(usize),
#[cfg(feature = "from_json")]
IsNull,
#[cfg(feature = "from_toml")]
IsDatetime,
TRUE,
FALSE
}
impl Condition {
pub fn and(self, other: Condition) -> Condition {
Condition::And(Box::new(self), Box::new(other))
}
pub fn or(self, other: Condition) -> Condition {
Condition::Or(Box::new(self), Box::new(other))
}
pub fn not(self) -> Condition {
Condition::Not(Box::new(self))
}
pub fn execute(&self, input: &super::CfgValue) -> Condition {
use Condition::*;
match self {
IsInt => input.is_int().into(),
IsFloat => input.is_float().into(),
IsStr => input.is_str().into(),
IsList => input.is_list().into(),
IsMap => input.is_map().into(),
IsBool => input.is_bool().into(),
TRUE => TRUE,
FALSE => FALSE,
And(x,y) => {
let res1 = x.execute(input);
let res2 = y.execute(input);
if res1.to_bool() && res2.to_bool() {
TRUE
} else {FALSE}
},
Or(x,y) => {
let res1 = x.execute(input);
let res2 = y.execute(input);
if res1.to_bool() || res2.to_bool() {
TRUE
} else {FALSE}
},
Not(x) => {
let res = x.execute(input);
if res.to_bool() { FALSE } else { TRUE }
},
IsExactlyInt(s) => input.as_int().map_or(false, |i| *i == *s).into(),
IsExactlyFloat(s) => input.as_float().map_or(false, |f| *f == *s).into(),
IsExactlyStr(s) => input.as_str().map_or(false, |st| *st == *s).into(),
IsExactlyList(s) => input.as_list().map_or(false, |l| *l == *s).into(),
IsExactlyMap(s) => input.as_map().map_or(false, |l| *l == *s).into(),
IsTrue => input.as_bool().map_or(false, |b| *b).into(),
IsListWith(s) => {
input.as_list().map(|list| {
for elem in list.iter() {
if !elem.check_that((**s).clone()) {
return FALSE;
}
}
TRUE
}).map_or(FALSE, |o| o.into())
},
IsListWithLength(l) => input.as_list().map_or(false, |li| *l == li.len()).into(),
#[cfg(feature = "from_json")]
IsNull => input.is_null().into(),
#[cfg(feature = "from_toml")]
IsDatetime => input.is_datetime().into(),
}
}
fn from_bool(b: bool) -> Condition {
if b {Condition::TRUE} else {Condition::FALSE}
}
pub fn to_bool(&self) -> bool {
if let Condition::TRUE = self { true } else { false }
}
}
impl BitAnd for Condition {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
self.and(rhs)
}
}
impl BitOr for Condition {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
self.or(rhs)
}
}
impl Not for Condition {
type Output = Self;
fn not(self) -> Self::Output {
self.not()
}
}
impl From<bool> for Condition {
fn from(b: bool) -> Self {
Condition::from_bool(b)
}
}
impl From<Condition> for bool {
fn from(c: Condition) -> Self {
c.to_bool()
}
}
#[cfg(test)]
mod test {
use crate::{CfgMap, CfgValue::*, Condition::*, Checkable};
#[test]
fn basic_and_exact() {
let i = Int(5);
let f = Float(2.0);
let s = Str(String::from("hello"));
let b = Bool(true);
let l = List(vec![Int(2), Float(8.0)]);
let m = Map(CfgMap::new());
assert!(i.check_that(IsInt));
assert!(i.check_that(IsExactlyInt(5)));
assert!(!i.check_that(IsExactlyInt(6)));
assert!(f.check_that(IsFloat));
assert!(f.check_that(IsExactlyFloat(2.0)));
assert!(!f.check_that(IsExactlyFloat(3.0)));
assert!(s.check_that(IsStr));
assert!(s.check_that(IsExactlyStr(String::from("hello"))));
assert!(!s.check_that(IsExactlyStr(String::from("hella"))));
assert!(b.check_that(IsBool));
assert!(b.check_that(IsTrue));
assert!(l.check_that(IsList));
assert!(l.check_that(IsExactlyList(vec![Int(2), Float(8.0)])));
assert!(!l.check_that(IsExactlyList(vec![Int(2), Float(8.1)])));
assert!(m.check_that(IsMap));
assert!(m.check_that(IsExactlyMap(CfgMap::new())));
let mut map = CfgMap::new();
map.default = "default".into();
assert!(!m.check_that(IsExactlyMap(map)));
}
#[test]
fn combinations() {
vec![Int(5), Float(9.0), Str(String::from("foobar"))]
.iter()
.for_each(|e| assert!(e.check_that(IsInt | IsFloat | IsStr)));
vec![Int(5), Float(9.0), Str(String::from("foobar"))]
.iter()
.for_each(|e| assert!(!e.check_that(IsList | IsMap)));
vec![Int(5), Float(9.0), Str(String::from("foobar"))]
.iter()
.for_each(|e| assert!(!e.check_that(IsInt & IsFloat)));
}
#[test]
fn misc() {
let listexample = List(vec![Int(5), Float(9.0)]);
assert!(listexample.check_that(IsListWith(Box::new(IsInt | IsFloat))));
assert!(listexample.check_that(IsListWithLength(2)));
assert!(!listexample.check_that(IsListWithLength(3)));
}
}