#![allow(dead_code)]
mod core;
mod error;
pub use crate::core::{
Condition, ConditionResult, Constraint, Engine, Event, EventParams, Rule, RuleResult, Status,
};
pub use error::{Error, Result};
pub fn and(and: Vec<Condition>) -> Condition {
Condition::And { and }
}
pub fn or(or: Vec<Condition>) -> Condition {
Condition::Or { or }
}
pub fn at_least(should_minimum_meet: usize, conditions: Vec<Condition>) -> Condition {
Condition::AtLeast {
should_minimum_meet,
conditions,
}
}
pub fn string_equals(field: &str, val: &str) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::StringEquals(val.into()),
}
}
pub fn string_not_equals(field: &str, val: &str) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::StringNotEquals(val.into()),
}
}
pub fn string_contains(field: &str, val: &str) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::StringContains(val.into()),
}
}
pub fn string_does_not_contains(field: &str, val: &str) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::StringDoesNotContain(val.into()),
}
}
pub fn string_in(field: &str, val: Vec<&str>) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::StringIn(val.into_iter().map(ToOwned::to_owned).collect()),
}
}
pub fn string_not_in(field: &str, val: Vec<&str>) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::StringNotIn(val.into_iter().map(ToOwned::to_owned).collect()),
}
}
pub fn int_equals(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntEquals(val),
}
}
pub fn int_not_equals(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntNotEquals(val),
}
}
pub fn int_contains(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntContains(val),
}
}
pub fn int_does_not_contain(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntDoesNotContain(val),
}
}
pub fn int_in(field: &str, val: Vec<i64>) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntIn(val),
}
}
pub fn int_not_in(field: &str, val: Vec<i64>) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntNotIn(val),
}
}
pub fn int_in_range(field: &str, start: i64, end: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntInRange(start, end),
}
}
pub fn int_not_in_range(field: &str, start: i64, end: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntNotInRange(start, end),
}
}
pub fn int_less_than(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntLessThan(val),
}
}
pub fn int_less_than_inclusive(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntLessThanInclusive(val),
}
}
pub fn int_greater_than(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntGreaterThan(val),
}
}
pub fn int_greater_than_inclusive(field: &str, val: i64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::IntGreaterThanInclusive(val),
}
}
pub fn float_equals(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatEquals(val),
}
}
pub fn float_not_equals(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatNotEquals(val),
}
}
pub fn float_contains(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatContains(val),
}
}
pub fn float_does_not_contain(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatDoesNotContain(val),
}
}
pub fn float_in(field: &str, val: Vec<f64>) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatIn(val),
}
}
pub fn float_not_in(field: &str, val: Vec<f64>) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatNotIn(val),
}
}
pub fn float_in_range(field: &str, start: f64, end: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatInRange(start, end),
}
}
pub fn float_not_in_range(field: &str, start: f64, end: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatNotInRange(start, end),
}
}
pub fn float_less_than(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatLessThan(val),
}
}
pub fn float_less_than_inclusive(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatLessThanInclusive(val),
}
}
pub fn float_greater_than(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatGreaterThan(val),
}
}
pub fn float_greater_than_inclusive(field: &str, val: f64) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::FloatGreaterThanInclusive(val),
}
}
pub fn bool_equals(field: &str, val: bool) -> Condition {
Condition::Condition {
field: field.into(),
constraint: Constraint::BoolEquals(val),
}
}
#[cfg(test)]
mod tests {
use super::{and, at_least, bool_equals, int_equals, int_in_range, or, string_equals, Status};
use serde_json::{json, Value};
fn get_test_data() -> Value {
json!({
"foo": 1,
"bar": "bar",
"baz": true
})
}
#[test]
fn and_rules() {
let map = get_test_data();
let mut root = and(vec![int_equals("foo", 1), string_equals("bar", "bar")]);
let mut res = root.check_value(&map);
assert!(res.status == Status::Met);
root = and(vec![int_equals("foo", 2), string_equals("bar", "bar")]);
res = root.check_value(&map);
assert!(res.status == Status::NotMet);
root = and(vec![int_equals("quux", 2), string_equals("bar", "bar")]);
res = root.check_value(&map);
assert!(res.status == Status::Unknown);
root = and(vec![int_equals("quux", 2), string_equals("bar", "baz")]);
res = root.check_value(&map);
assert!(res.status == Status::NotMet);
root = and(vec![int_equals("quux", 2), string_equals("fizz", "bar")]);
res = root.check_value(&map);
assert!(res.status == Status::Unknown);
}
#[test]
fn or_rules() {
let map = get_test_data();
let mut root = or(vec![int_equals("foo", 1), string_equals("bar", "bar")]);
let mut res = root.check_value(&map);
assert!(res.status == Status::Met);
root = or(vec![int_equals("foo", 2), string_equals("bar", "bar")]);
res = root.check_value(&map);
assert!(res.status == Status::Met);
root = or(vec![int_equals("quux", 2), string_equals("bar", "bar")]);
res = root.check_value(&map);
assert!(res.status == Status::Met);
root = or(vec![int_equals("quux", 2), string_equals("bar", "baz")]);
res = root.check_value(&map);
assert!(res.status == Status::Unknown);
root = or(vec![int_equals("quux", 2), string_equals("fizz", "bar")]);
res = root.check_value(&map);
assert!(res.status == Status::Unknown);
}
#[test]
fn n_of_rules() {
let map = get_test_data();
let mut root = at_least(
2,
vec![
int_equals("foo", 1),
string_equals("bar", "bar"),
bool_equals("baz", false),
],
);
let mut res = root.check_value(&map);
assert!(res.status == Status::Met);
root = at_least(
2,
vec![
int_equals("foo", 1),
string_equals("quux", "bar"),
bool_equals("baz", false),
],
);
res = root.check_value(&map);
assert!(res.status == Status::NotMet);
root = at_least(
2,
vec![
int_equals("foo", 2),
string_equals("quux", "baz"),
bool_equals("baz", false),
],
);
res = root.check_value(&map);
assert!(res.status == Status::NotMet);
}
#[test]
fn string_equals_rule() {
let map = get_test_data();
let mut rule = string_equals("bar", "bar");
let mut res = rule.check_value(&map);
assert!(res.status == Status::Met);
rule = string_equals("bar", "baz");
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
}
#[test]
fn int_equals_rule() {
let map = get_test_data();
let mut rule = int_equals("foo", 1);
let mut res = rule.check_value(&map);
assert!(res.status == Status::Met);
rule = int_equals("foo", 2);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
rule = int_equals("bar", 2);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
}
#[test]
fn int_range_rule() {
let map = get_test_data();
let mut rule = int_in_range("foo", 1, 3);
let mut res = rule.check_value(&map);
assert!(res.status == Status::Met);
rule = int_in_range("foo", 2, 3);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
rule = int_in_range("bar", 1, 3);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
}
#[test]
fn boolean_rule() {
let mut map = get_test_data();
let mut rule = bool_equals("baz", true);
let mut res = rule.check_value(&map);
assert!(res.status == Status::Met);
rule = bool_equals("baz", false);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
rule = bool_equals("bar", true);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
rule = bool_equals("bar", false);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
map["quux".to_owned()] = json!("tRuE");
rule = bool_equals("quux", true);
res = rule.check_value(&map);
assert!(res.status == Status::NotMet);
}
}