use serde::{Deserialize, Serialize};
use strum::Display;
#[derive(Display, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Predicate {
#[strum(serialize = "should be")]
#[serde(rename = "should be")]
Is,
#[strum(serialize = "should not be")]
#[serde(rename = "should not be")]
IsNot,
#[strum(serialize = "should contain")]
#[serde(rename = "should contain")]
Contains,
#[strum(serialize = "should not contain")]
#[serde(rename = "should not contain")]
DoesNotContain,
#[strum(serialize = "should match")]
#[serde(rename = "should match")]
Matches,
#[strum(serialize = "should not match")]
#[serde(rename = "should not match")]
DoesNotMatch,
#[strum(serialize = "should be less than")]
#[serde(rename = "should be less than")]
LessThan,
#[strum(serialize = "should be between")]
#[serde(rename = "should be between")]
Between,
#[strum(serialize = "should match schema")]
#[serde(rename = "should match schema")]
Schema,
#[strum(serialize = "none")]
#[serde(rename = "none")]
NoPredicate,
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Range<T> {
pub left: T,
pub right: T,
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct RegexWrapper<T>(pub T);
#[derive(Deserialize, Debug, PartialEq, Eq)]
pub struct Expression<T> {
pub predicate: Predicate,
pub value: T,
}
macro_rules! predicate {
($(#[$meta:meta])* $name:ident, $o:expr) => {
$(#[$meta])*
pub fn $name<T>(value: T) -> Expression<T> {
Expression {
predicate: $o,
value,
}
}
};
}
pub fn is_between<T>(min: T, max: T) -> Expression<Range<T>> {
Expression {
predicate: Predicate::Between,
value: Range {
left: min,
right: max,
},
}
}
pub fn matches<T>(re: T) -> Expression<RegexWrapper<T>> {
Expression {
predicate: Predicate::Matches,
value: RegexWrapper(re),
}
}
pub fn does_not_match<T>(re: T) -> Expression<RegexWrapper<T>> {
Expression {
predicate: Predicate::DoesNotMatch,
value: RegexWrapper(re),
}
}
predicate!(
is,
Predicate::Is
);
predicate!(
is_not,
Predicate::IsNot
);
predicate!(
contains,
Predicate::Contains
);
predicate!(
does_not_contain,
Predicate::DoesNotContain
);
predicate!(
is_less_than,
Predicate::LessThan
);
predicate!(
schema,
Predicate::Schema
);
#[cfg(test)]
mod tests {
use super::{Expression, Predicate, Range};
use serde_json::Value;
use test_case::test_case;
#[test_case(Value::String(String::from("should be")), Predicate::Is; "Failed to deserialize predicate Is")]
#[test_case(Value::String(String::from("should not be")), Predicate::IsNot; "Failed to deserialize predicate IsNot")]
#[test_case(Value::String(String::from("should contain")), Predicate::Contains; "Failed to deserialize predicate Contains")]
#[test_case(Value::String(String::from("should not contain")), Predicate::DoesNotContain; "Failed to deserialize predicate DoesNotContain")]
#[test_case(Value::String(String::from("should match")), Predicate::Matches; "Failed to deserialize predicate Matches")]
#[test_case(Value::String(String::from("should not match")), Predicate::DoesNotMatch; "Failed to deserialize predicate DoesNotMatch")]
#[test_case(Value::String(String::from("should be less than")), Predicate::LessThan; "Failed to deserialize predicate LessThan")]
#[test_case(Value::String(String::from("should be between")), Predicate::Between; "Failed to deserialize predicate Between")]
#[test_case(Value::String(String::from("should match schema")), Predicate::Schema; "Failed to deserialize predicate Schema")]
fn deser_predicates(json_predicate: Value, predicate: Predicate) {
assert_eq!(
serde_json::from_value::<Predicate>(json_predicate).unwrap(),
predicate
)
}
#[test]
fn deser_expression() {
let json = serde_json::json!({
"predicate": "should be between",
"value": {
"left": 200,
"right": 299
}
});
let expr: Expression<Range<u16>> = serde_json::from_value(json).unwrap();
assert_eq!(
expr,
Expression {
predicate: Predicate::Between,
value: Range {
left: 200,
right: 299
}
}
);
}
}