rust_rule_engine/engine/
rule.rs1use crate::types::{ActionType, LogicalOperator, Operator, Value};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone)]
6pub struct Condition {
7 pub field: String,
9 pub operator: Operator,
11 pub value: Value,
13}
14
15impl Condition {
16 pub fn new(field: String, operator: Operator, value: Value) -> Self {
18 Self {
19 field,
20 operator,
21 value,
22 }
23 }
24
25 pub fn evaluate(&self, facts: &HashMap<String, Value>) -> bool {
27 if let Some(field_value) = get_nested_value(facts, &self.field) {
28 self.operator.evaluate(field_value, &self.value)
30 } else {
31 false
32 }
33 }
34}
35
36#[derive(Debug, Clone)]
38pub enum ConditionGroup {
39 Single(Condition),
41 Compound {
43 left: Box<ConditionGroup>,
45 operator: LogicalOperator,
47 right: Box<ConditionGroup>,
49 },
50 Not(Box<ConditionGroup>),
52}
53
54impl ConditionGroup {
55 pub fn single(condition: Condition) -> Self {
57 ConditionGroup::Single(condition)
58 }
59
60 pub fn and(left: ConditionGroup, right: ConditionGroup) -> Self {
62 ConditionGroup::Compound {
63 left: Box::new(left),
64 operator: LogicalOperator::And,
65 right: Box::new(right),
66 }
67 }
68
69 pub fn or(left: ConditionGroup, right: ConditionGroup) -> Self {
71 ConditionGroup::Compound {
72 left: Box::new(left),
73 operator: LogicalOperator::Or,
74 right: Box::new(right),
75 }
76 }
77
78 #[allow(clippy::should_implement_trait)]
80 pub fn not(condition: ConditionGroup) -> Self {
81 ConditionGroup::Not(Box::new(condition))
82 }
83
84 pub fn evaluate(&self, facts: &HashMap<String, Value>) -> bool {
86 match self {
87 ConditionGroup::Single(condition) => condition.evaluate(facts),
88 ConditionGroup::Compound {
89 left,
90 operator,
91 right,
92 } => {
93 let left_result = left.evaluate(facts);
94 let right_result = right.evaluate(facts);
95 match operator {
96 LogicalOperator::And => left_result && right_result,
97 LogicalOperator::Or => left_result || right_result,
98 LogicalOperator::Not => !left_result, }
100 }
101 ConditionGroup::Not(condition) => !condition.evaluate(facts),
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
108pub struct Rule {
109 pub name: String,
111 pub description: Option<String>,
113 pub salience: i32,
115 pub enabled: bool,
117 pub conditions: ConditionGroup,
119 pub actions: Vec<ActionType>,
121}
122
123impl Rule {
124 pub fn new(name: String, conditions: ConditionGroup, actions: Vec<ActionType>) -> Self {
126 Self {
127 name,
128 description: None,
129 salience: 0,
130 enabled: true,
131 conditions,
132 actions,
133 }
134 }
135
136 pub fn with_description(mut self, description: String) -> Self {
138 self.description = Some(description);
139 self
140 }
141
142 pub fn with_salience(mut self, salience: i32) -> Self {
144 self.salience = salience;
145 self
146 }
147
148 pub fn with_priority(mut self, priority: i32) -> Self {
150 self.salience = priority;
151 self
152 }
153
154 pub fn matches(&self, facts: &HashMap<String, Value>) -> bool {
156 self.enabled && self.conditions.evaluate(facts)
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct RuleExecutionResult {
163 pub rule_name: String,
165 pub matched: bool,
167 pub actions_executed: Vec<String>,
169 pub execution_time_ms: f64,
171}
172
173impl RuleExecutionResult {
174 pub fn new(rule_name: String) -> Self {
176 Self {
177 rule_name,
178 matched: false,
179 actions_executed: Vec::new(),
180 execution_time_ms: 0.0,
181 }
182 }
183
184 pub fn matched(mut self) -> Self {
186 self.matched = true;
187 self
188 }
189
190 pub fn with_actions(mut self, actions: Vec<String>) -> Self {
192 self.actions_executed = actions;
193 self
194 }
195
196 pub fn with_execution_time(mut self, time_ms: f64) -> Self {
198 self.execution_time_ms = time_ms;
199 self
200 }
201}
202
203fn get_nested_value<'a>(data: &'a HashMap<String, Value>, path: &str) -> Option<&'a Value> {
205 let parts: Vec<&str> = path.split('.').collect();
206 let mut current = data.get(parts[0])?;
207
208 for part in parts.iter().skip(1) {
209 match current {
210 Value::Object(obj) => {
211 current = obj.get(*part)?;
212 }
213 _ => return None,
214 }
215 }
216
217 Some(current)
218}