Skip to main content

RuleEngine

Struct RuleEngine 

Source
pub struct RuleEngine { /* private fields */ }
Expand description

Runs rules against facts, as Grule’s engine does.

An engine holds a set of rules (its KnowledgeBase) plus registered functions, methods and listeners. execute takes &self, so one engine can be shared between threads and run concurrently.

Implementations§

Source§

impl RuleEngine

Source

pub fn new() -> Self

An engine with no rules, Grule’s default cycle limit (5000), and failed conditions counting as false.

Source

pub fn add_rule(&mut self, rule: Rule) -> Result<(), String>

Adds a rule. Fails if a rule with the same name exists.

Source

pub fn add_rules_from_str(&mut self, grl: &str) -> Result<usize, String>

Parses GRL text and adds all of its rules. Returns how many were added.

Source

pub fn add_rules_from_file( &mut self, path: impl AsRef<Path>, ) -> Result<usize, String>

Loads all rules from a .grl file. Returns how many were added.

Source

pub fn add_rules_from_dir( &mut self, dir: impl AsRef<Path>, ) -> Result<usize, String>

Loads all .grl files under dir, recursively. Returns how many rules were added.

Source

pub fn set_max_cycles(&mut self, max_cycles: usize)

Sets the maximum number of rule firings per execution before it fails with EngineError::MaxCyclesExceeded.

Source

pub fn set_return_err_on_failed_rule_evaluation(&mut self, enabled: bool)

By default, as in Grule, a condition that fails to evaluate (a missing field, a type error, …) is logged and treated as false. When enabled, execution stops with the error instead.

Source

pub fn set_condition_caching(&mut self, enabled: bool)

Reusing condition results between cycles is on by default and never changes which rules fire; turning it off is only useful for comparison and debugging.

Source

pub fn add_listener(&mut self, listener: impl EngineListener + 'static)

Adds a listener notified as executions run. With any listener attached, every rule is evaluated every cycle (as in Grule) so that listeners see each evaluation.

Source

pub fn register_function<F>( &mut self, name: &str, function: F, ) -> Result<(), String>
where F: Fn(&[FactValue]) -> Result<FactValue, String> + Send + Sync + 'static,

Makes function callable from rules as name(args...).

Source

pub fn register_method<F>( &mut self, name: &str, method: F, ) -> Result<(), String>
where F: Fn(&mut FactValue, &[FactValue]) -> Result<FactValue, String> + Send + Sync + 'static,

Makes method callable from rules as receiver.name(args...) on any value that has no built-in method of that name. This is the equivalent of calling a method on a Go fact in Grule: when called as a statement in a rule’s then scope on a fact path (Order.AddItem(x);), the method receives the stored value and its changes persist. Anywhere else it receives a copy.

Source

pub fn register_typed_method<T, A, R, F>( &mut self, name: &str, method: F, ) -> Result<(), String>
where T: Serialize + DeserializeOwned + 'static, A: DeserializeOwned + 'static, R: Serialize + 'static, F: Fn(&mut T, A) -> Result<R, String> + Send + Sync + 'static,

Like register_method, but the method works on your own type T, the counterpart of a method on a Go fact struct. Around each call the receiver is converted into T and back with serde (so T is typically the type the fact was created from with Fact::from_serialize), the arguments are decoded into A, and the result R is converted into a fact value.

A is a tuple for fixed arguments ((f64,), (String, i64)), () for none, or a Vec for any number. Return Ok(()) for no result (it becomes nil).

The same name can be registered for several types; a call goes to the first whose type the receiver converts into, then to an untyped method of that name. Fields the receiver has that T doesn’t are kept.

let mut engine = RuleEngine::new();
engine.register_typed_method("CalculateEscrow", |loan: &mut Loan, (): ()| {
    loan.escrow = loan.amount * 0.01;
    Ok(())
})?;
engine.register_typed_method("Payment", |loan: &mut Loan, (rate,): (f64,)| {
    Ok(loan.amount * rate / 12.0)
})?;
// GRL: then Loan.CalculateEscrow(); Loan.Monthly = Loan.Payment(0.06);
Source

pub fn execute( &self, facts: &mut HashMap<String, Fact>, ) -> Result<ExecutionResult, EngineError>

Runs rules until no condition holds, as Grule does. Each cycle evaluates every rule’s condition, then fires the highest-salience rule among those that matched (ties go to the rule added first). Rules removed with Retract stay out for the rest of this execution, and Complete() ends it once the current rule’s actions have run.

Two optimizations keep this from re-evaluating everything every cycle, without changing which rules fire:

  • A condition’s result is reused until a rule’s actions (or Changed/Forget) touch a fact path it reads. Conditions using Now(), logging or user functions/methods are always re-evaluated.
  • Once the cycle’s highest-salience match is found, lower rules are skipped unless evaluating them has observable effects: a listener is attached, evaluation errors are returned, or the condition logs or calls user code.
Source

pub fn execute_with_cancel( &self, facts: &mut HashMap<String, Fact>, cancel: &AtomicBool, ) -> Result<ExecutionResult, EngineError>

Like execute, but stops with EngineError::Cancelled once cancel is set, checked before each rule evaluation. Set it from another thread.

Source

pub fn execute_with_timeout( &self, facts: &mut HashMap<String, Fact>, timeout: Duration, ) -> Result<ExecutionResult, EngineError>

Like execute, but stops with EngineError::TimedOut once timeout has elapsed, checked before each rule evaluation (like a Go context deadline). A single slow function call can still overrun it.

Source

pub fn fetch_matching_rules( &self, facts: &HashMap<String, Fact>, ) -> Result<Vec<&Rule>, EngineError>

Returns the rules whose conditions currently hold, highest salience first, without running any of them (Grule’s FetchMatchingRules).

Source

pub fn get_knowledge_base(&self) -> &KnowledgeBase

The engine’s rules.

Trait Implementations§

Source§

impl Default for RuleEngine

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.