use std::{collections::HashMap, fmt};
#[derive(Debug, Clone, PartialEq)]
pub enum RuleError {
ContextNotSet,
TypeMismatch {
key: &'static str,
expected: &'static str,
},
TooManyChildren { max: usize, attempted: usize },
ExecutionFailed(String),
BorrowFailed(String),
}
impl fmt::Display for RuleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RuleError::ContextNotSet => write!(f, "Rule context was not set"),
RuleError::TypeMismatch { key, expected } => {
write!(f, "Type mismatch for key '{}': expected {}", key, expected)
}
RuleError::TooManyChildren { max, attempted } => {
write!(
f,
"Too many children: max {} but attempted {}",
max, attempted
)
}
RuleError::ExecutionFailed(msg) => write!(f, "Rule execution failed: {}", msg),
RuleError::BorrowFailed(msg) => write!(f, "Borrow check failed: {}", msg),
}
}
}
impl std::error::Error for RuleError {}
pub type RuleResult<T> = Result<T, RuleError>;
pub type EvalFn = Box<dyn Fn(&RuleContext) -> RuleResult<bool>>;
pub type ExecuteFn = Box<dyn Fn(&mut RuleContext) -> RuleResult<()>>;
pub use crate::engine::Engine;
pub use crate::rule::best_first_rule::BestFirstRule;
pub use crate::rule::chain_rule::ChainRule;
pub use crate::runner::RuleRunner;
pub(crate) mod best_first_rule;
pub(crate) mod chain_rule;
#[derive(Debug)]
pub enum ContextValue {
Bool(bool),
Int(i64),
Float(f64),
String(String),
Bytes(Vec<u8>),
}
impl ContextValue {
pub fn as_bool(&self) -> RuleResult<bool> {
match self {
ContextValue::Bool(v) => Ok(*v),
_ => Err(RuleError::TypeMismatch {
key: "unknown",
expected: "bool",
}),
}
}
pub fn as_int(&self) -> RuleResult<i64> {
match self {
ContextValue::Int(v) => Ok(*v),
_ => Err(RuleError::TypeMismatch {
key: "unknown",
expected: "i64",
}),
}
}
pub fn as_float(&self) -> RuleResult<f64> {
match self {
ContextValue::Float(v) => Ok(*v),
_ => Err(RuleError::TypeMismatch {
key: "unknown",
expected: "f64",
}),
}
}
pub fn as_string(&self) -> RuleResult<&str> {
match self {
ContextValue::String(v) => Ok(v),
_ => Err(RuleError::TypeMismatch {
key: "unknown",
expected: "String",
}),
}
}
pub fn as_bytes(&self) -> RuleResult<&[u8]> {
match self {
ContextValue::Bytes(v) => Ok(v),
_ => Err(RuleError::TypeMismatch {
key: "unknown",
expected: "Vec<u8>",
}),
}
}
}
#[derive(Debug, Default)]
pub struct RuleContext {
context_map: HashMap<&'static str, ContextValue>,
}
impl RuleContext {
pub fn new() -> Self {
RuleContext {
context_map: HashMap::new(),
}
}
pub fn set_bool(&mut self, key: &'static str, value: bool) {
self.context_map.insert(key, ContextValue::Bool(value));
}
pub fn set_int(&mut self, key: &'static str, value: i64) {
self.context_map.insert(key, ContextValue::Int(value));
}
pub fn set_float(&mut self, key: &'static str, value: f64) {
self.context_map.insert(key, ContextValue::Float(value));
}
pub fn set_string(&mut self, key: &'static str, value: String) {
self.context_map.insert(key, ContextValue::String(value));
}
pub fn set_bytes(&mut self, key: &'static str, value: Vec<u8>) {
self.context_map.insert(key, ContextValue::Bytes(value));
}
pub fn get_bool(&self, key: &'static str) -> RuleResult<bool> {
self.context_map
.get(key)
.ok_or(RuleError::TypeMismatch {
key,
expected: "bool",
})?
.as_bool()
}
pub fn get_int(&self, key: &'static str) -> RuleResult<i64> {
self.context_map
.get(key)
.ok_or(RuleError::TypeMismatch {
key,
expected: "i64",
})?
.as_int()
}
pub fn get_float(&self, key: &'static str) -> RuleResult<f64> {
self.context_map
.get(key)
.ok_or(RuleError::TypeMismatch {
key,
expected: "f64",
})?
.as_float()
}
pub fn get_string(&self, key: &'static str) -> RuleResult<&str> {
self.context_map
.get(key)
.ok_or(RuleError::TypeMismatch {
key,
expected: "String",
})?
.as_string()
}
pub fn get_bytes(&self, key: &'static str) -> RuleResult<&[u8]> {
self.context_map
.get(key)
.ok_or(RuleError::TypeMismatch {
key,
expected: "Vec<u8>",
})?
.as_bytes()
}
pub fn contains_key(&self, key: &'static str) -> bool {
self.context_map.contains_key(key)
}
pub fn remove(&mut self, key: &'static str) -> Option<ContextValue> {
self.context_map.remove(key)
}
pub fn clear(&mut self) {
self.context_map.clear();
}
}
pub trait Rule {
fn evaluate(&self, context: &RuleContext) -> RuleResult<bool>;
fn execute(&mut self, context: &mut RuleContext) -> RuleResult<()>;
fn children(&self) -> &[Box<dyn Rule>];
fn children_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
fn add_child(&mut self, child: Box<dyn Rule>) -> RuleResult<()>;
fn add_children(&mut self, children: Vec<Box<dyn Rule>>) -> RuleResult<()> {
for child in children {
self.add_child(child)?;
}
Ok(())
}
fn fire(&mut self, context: &mut RuleContext) -> RuleResult<bool> {
if self.evaluate(context)? {
self.execute(context)?;
for child in self.children_mut() {
child.fire(context)?;
}
Ok(true)
} else {
Ok(false)
}
}
}
pub struct BaseRule {
children: Vec<Box<dyn Rule>>,
eval_fn: Option<EvalFn>,
pre_execute_fn: Option<ExecuteFn>,
execute_fn: Option<ExecuteFn>,
post_execute_fn: Option<ExecuteFn>,
}
impl BaseRule {
pub fn new() -> Self {
BaseRule {
children: Vec::new(),
eval_fn: None,
pre_execute_fn: None,
execute_fn: None,
post_execute_fn: None,
}
}
pub fn set_eval_fn<F>(&mut self, f: F)
where
F: Fn(&RuleContext) -> RuleResult<bool> + 'static,
{
self.eval_fn = Some(Box::new(f));
}
pub fn set_pre_execute_fn<F>(&mut self, f: F)
where
F: Fn(&mut RuleContext) -> RuleResult<()> + 'static,
{
self.pre_execute_fn = Some(Box::new(f));
}
pub fn set_execute_fn<F>(&mut self, f: F)
where
F: Fn(&mut RuleContext) -> RuleResult<()> + 'static,
{
self.execute_fn = Some(Box::new(f));
}
pub fn set_post_execute_fn<F>(&mut self, f: F)
where
F: Fn(&mut RuleContext) -> RuleResult<()> + 'static,
{
self.post_execute_fn = Some(Box::new(f));
}
}
impl Rule for BaseRule {
fn evaluate(&self, context: &RuleContext) -> RuleResult<bool> {
match &self.eval_fn {
Some(f) => f(context),
None => Ok(true), }
}
fn execute(&mut self, context: &mut RuleContext) -> RuleResult<()> {
if let Some(f) = &self.pre_execute_fn {
f(context)?;
}
if let Some(f) = &self.execute_fn {
f(context)?;
}
if let Some(f) = &self.post_execute_fn {
f(context)?;
}
Ok(())
}
fn children(&self) -> &[Box<dyn Rule>] {
&self.children
}
fn children_mut(&mut self) -> &mut Vec<Box<dyn Rule>> {
&mut self.children
}
fn add_child(&mut self, child: Box<dyn Rule>) -> RuleResult<()> {
self.children.push(child);
Ok(())
}
}
impl Default for BaseRule {
fn default() -> Self {
Self::new()
}
}