1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
//! Reval, short for Rust Evaluator is a light-weight expression evaluator library. It can be used as a rules-engine or in other situations where simple user expressions need to be evaluated.
//!
//! Expressions or rules can be written in a simple DSL or in a json format that is directly parsed into Reval expression AST objects. For now the Reval DSL parser is experimental but eventually this should replace json rules.
//!
//! Expressions can be evaluated against input data to produce output data. The input and output data is passed in and out of the rules as a `reval::Value` object which can contain simple data or more complex structures like maps or vectors.
//! Data can be nested so complex data can be passed into Reval expressions.
//!
//! To make it easy to construct input data from your own datatypes the Reval crate implements a serde serializer for Value types. So any type that implements `serde::Serialize` can be serialized into a `reval::Value` without writing any code.
//!
//! More information on writing rules can be found here: [Writing Rules](https://mendelt.github.io/reval/)
//!
//! This example shows how to set up a RuleSet using the builder. A simple Rule is added and a data-type is set up that is used as input for the rules;
//! ```rust
//! # tokio_test::block_on(async {
//! use reval::prelude::*;
//! use serde::Serialize;
//!
//! // The input data-type for the rules, must implement serde::Serialize so it
//! // can be serialized into a `reval::Value`
//! #[derive(Serialize)]
//! struct Data {
//! age: u16,
//! }
//! // Set up an "age check" rule that checks if the "age" input field is
//! // greater than or equal to 21
//! let rule = r#"
//! {
//! "name": "age check",
//! "expr": {
//! "gte": [
//! {"ref": "age"},
//! {"int": 21}
//! ]
//! }
//! }"#;
//! // Set up the ruleset builder, add the rule and build the `RuleSet`
//! let ruleset = ruleset().with_rule(Rule::parse_json(rule).unwrap()).unwrap().build();
//! // Set up input data
//! let facts = Data { age: 16 };
//! // Evaluate the ruleset on the input data and check if the rule returns
//! // `false`
//! for outcome in ruleset.evaluate(&facts).await.unwrap() {
//! assert_eq!(outcome.value.unwrap(), false.into());
//! }
//! # })
//! ```
//!
//! Reval can be extended with user-functions by implementing the `UserFunction` trait on a type and passing an instance of that type to the RuleSet. The following example shows how this would work;
//! The input data-type for the rules, must implement serde::Serialize so it
//! can be serialized into a `reval::Value`
//! ```rust
//! # tokio_test::block_on(async {
//! use reval::prelude::*;
//! use serde::Serialize;
//!
//! #[derive(Serialize)]
//! struct Data {
//! age: u16,
//! }
//! // Set up a FakeId UserFunction that increments an integer `Value` to
//! // bypass the age check
//! struct FakeId;
//! #[async_trait::async_trait]
//! impl UserFunction for FakeId {
//! async fn call(&self, param: Value) -> FunctionResult {
//! let age: i128 = param.try_into()?;
//! Ok((age * 2).into())
//! }
//!
//! fn name(&self) -> &'static str {
//! "fake_id"
//! }
//! }
//! // Set up an "age check" rule that checks if the "age" input field is
//! // greater than or equal to 21. But it first calls the `fake_id` user-
//! // function.
//! let rule = r#"
//! {
//! "name": "age check",
//! "expr": {
//! "gte": [
//! {"func": ["fake_id", {"ref": "age"}]},
//! {"int": 21}
//! ]
//! }
//! }"#;
//! // Set up the ruleset builder, add the rule, add the user-function and
//! // build the `RuleSet`
//! let ruleset = ruleset()
//! .with_rule(Rule::parse_json(rule).unwrap()).unwrap()
//! .with_function(FakeId {}).unwrap()
//! .build();
//! // Set up input data
//! let facts = Data { age: 16 };
//! // Evaluate the ruleset on the input data and check if the rule returns
//! // `true`
//! for outcome in ruleset.evaluate(&facts).await.unwrap() {
//! assert_eq!(outcome.value.unwrap(), true.into());
//! }
//! # })
//! ```
pub mod error;
pub mod expr;
pub mod function;
#[cfg(feature = "nom_parser")]
pub mod parse;
#[cfg(feature = "json_parser")]
pub mod parse_json;
pub mod ruleset;
pub mod value;
pub use error::{Error, Result};
pub mod prelude {
pub use crate::{
expr::Expr,
function::{FunctionResult, UserFunction},
ruleset::{builder::ruleset, rule::Rule, RuleSet},
value::Value,
};
}