1use std::{collections::HashMap, fmt};
2
3#[derive(Debug, Clone, PartialEq)]
5pub enum RuleError {
6 ContextNotSet,
8 TypeMismatch {
10 key: &'static str,
11 expected: &'static str,
12 },
13 TooManyChildren { max: usize, attempted: usize },
15 ExecutionFailed(String),
17 BorrowFailed(String),
19}
20
21impl fmt::Display for RuleError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 RuleError::ContextNotSet => write!(f, "Rule context was not set"),
25 RuleError::TypeMismatch { key, expected } => {
26 write!(f, "Type mismatch for key '{}': expected {}", key, expected)
27 }
28 RuleError::TooManyChildren { max, attempted } => {
29 write!(
30 f,
31 "Too many children: max {} but attempted {}",
32 max, attempted
33 )
34 }
35 RuleError::ExecutionFailed(msg) => write!(f, "Rule execution failed: {}", msg),
36 RuleError::BorrowFailed(msg) => write!(f, "Borrow check failed: {}", msg),
37 }
38 }
39}
40
41impl std::error::Error for RuleError {}
42
43pub type RuleResult<T> = Result<T, RuleError>;
45
46pub type EvalFn = Box<dyn Fn(&RuleContext) -> RuleResult<bool>>;
48pub type ExecuteFn = Box<dyn Fn(&mut RuleContext) -> RuleResult<()>>;
49
50pub use crate::engine::Engine;
51pub use crate::rule::best_first_rule::BestFirstRule;
52pub use crate::rule::chain_rule::ChainRule;
53pub use crate::runner::RuleRunner;
54
55pub(crate) mod best_first_rule;
56pub(crate) mod chain_rule;
57
58#[derive(Debug)]
61pub enum ContextValue {
62 Bool(bool),
63 Int(i64),
64 Float(f64),
65 String(String),
66 Bytes(Vec<u8>),
67}
68
69impl ContextValue {
70 pub fn as_bool(&self) -> RuleResult<bool> {
72 match self {
73 ContextValue::Bool(v) => Ok(*v),
74 _ => Err(RuleError::TypeMismatch {
75 key: "unknown",
76 expected: "bool",
77 }),
78 }
79 }
80
81 pub fn as_int(&self) -> RuleResult<i64> {
83 match self {
84 ContextValue::Int(v) => Ok(*v),
85 _ => Err(RuleError::TypeMismatch {
86 key: "unknown",
87 expected: "i64",
88 }),
89 }
90 }
91
92 pub fn as_float(&self) -> RuleResult<f64> {
94 match self {
95 ContextValue::Float(v) => Ok(*v),
96 _ => Err(RuleError::TypeMismatch {
97 key: "unknown",
98 expected: "f64",
99 }),
100 }
101 }
102
103 pub fn as_string(&self) -> RuleResult<&str> {
105 match self {
106 ContextValue::String(v) => Ok(v),
107 _ => Err(RuleError::TypeMismatch {
108 key: "unknown",
109 expected: "String",
110 }),
111 }
112 }
113
114 pub fn as_bytes(&self) -> RuleResult<&[u8]> {
116 match self {
117 ContextValue::Bytes(v) => Ok(v),
118 _ => Err(RuleError::TypeMismatch {
119 key: "unknown",
120 expected: "Vec<u8>",
121 }),
122 }
123 }
124}
125
126#[derive(Debug, Default)]
138pub struct RuleContext {
139 context_map: HashMap<&'static str, ContextValue>,
140}
141
142impl RuleContext {
143 pub fn new() -> Self {
144 RuleContext {
145 context_map: HashMap::new(),
146 }
147 }
148
149 pub fn set_bool(&mut self, key: &'static str, value: bool) {
151 self.context_map.insert(key, ContextValue::Bool(value));
152 }
153
154 pub fn set_int(&mut self, key: &'static str, value: i64) {
156 self.context_map.insert(key, ContextValue::Int(value));
157 }
158
159 pub fn set_float(&mut self, key: &'static str, value: f64) {
161 self.context_map.insert(key, ContextValue::Float(value));
162 }
163
164 pub fn set_string(&mut self, key: &'static str, value: String) {
166 self.context_map.insert(key, ContextValue::String(value));
167 }
168
169 pub fn set_bytes(&mut self, key: &'static str, value: Vec<u8>) {
171 self.context_map.insert(key, ContextValue::Bytes(value));
172 }
173
174 pub fn get_bool(&self, key: &'static str) -> RuleResult<bool> {
176 self.context_map
177 .get(key)
178 .ok_or(RuleError::TypeMismatch {
179 key,
180 expected: "bool",
181 })?
182 .as_bool()
183 }
184
185 pub fn get_int(&self, key: &'static str) -> RuleResult<i64> {
187 self.context_map
188 .get(key)
189 .ok_or(RuleError::TypeMismatch {
190 key,
191 expected: "i64",
192 })?
193 .as_int()
194 }
195
196 pub fn get_float(&self, key: &'static str) -> RuleResult<f64> {
198 self.context_map
199 .get(key)
200 .ok_or(RuleError::TypeMismatch {
201 key,
202 expected: "f64",
203 })?
204 .as_float()
205 }
206
207 pub fn get_string(&self, key: &'static str) -> RuleResult<&str> {
209 self.context_map
210 .get(key)
211 .ok_or(RuleError::TypeMismatch {
212 key,
213 expected: "String",
214 })?
215 .as_string()
216 }
217
218 pub fn get_bytes(&self, key: &'static str) -> RuleResult<&[u8]> {
220 self.context_map
221 .get(key)
222 .ok_or(RuleError::TypeMismatch {
223 key,
224 expected: "Vec<u8>",
225 })?
226 .as_bytes()
227 }
228
229 pub fn contains_key(&self, key: &'static str) -> bool {
231 self.context_map.contains_key(key)
232 }
233
234 pub fn remove(&mut self, key: &'static str) -> Option<ContextValue> {
236 self.context_map.remove(key)
237 }
238
239 pub fn clear(&mut self) {
241 self.context_map.clear();
242 }
243}
244
245pub trait Rule {
247 fn evaluate(&self, context: &RuleContext) -> RuleResult<bool>;
249
250 fn execute(&mut self, context: &mut RuleContext) -> RuleResult<()>;
252
253 fn children(&self) -> &[Box<dyn Rule>];
255
256 fn children_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
258
259 fn add_child(&mut self, child: Box<dyn Rule>) -> RuleResult<()>;
261
262 fn add_children(&mut self, children: Vec<Box<dyn Rule>>) -> RuleResult<()> {
264 for child in children {
265 self.add_child(child)?;
266 }
267 Ok(())
268 }
269
270 fn fire(&mut self, context: &mut RuleContext) -> RuleResult<bool> {
272 if self.evaluate(context)? {
273 self.execute(context)?;
274
275 for child in self.children_mut() {
277 child.fire(context)?;
278 }
279
280 Ok(true)
281 } else {
282 Ok(false)
283 }
284 }
285}
286
287pub struct BaseRule {
289 children: Vec<Box<dyn Rule>>,
290 eval_fn: Option<EvalFn>,
291 pre_execute_fn: Option<ExecuteFn>,
292 execute_fn: Option<ExecuteFn>,
293 post_execute_fn: Option<ExecuteFn>,
294}
295
296impl BaseRule {
297 pub fn new() -> Self {
298 BaseRule {
299 children: Vec::new(),
300 eval_fn: None,
301 pre_execute_fn: None,
302 execute_fn: None,
303 post_execute_fn: None,
304 }
305 }
306
307 pub fn set_eval_fn<F>(&mut self, f: F)
309 where
310 F: Fn(&RuleContext) -> RuleResult<bool> + 'static,
311 {
312 self.eval_fn = Some(Box::new(f));
313 }
314
315 pub fn set_pre_execute_fn<F>(&mut self, f: F)
317 where
318 F: Fn(&mut RuleContext) -> RuleResult<()> + 'static,
319 {
320 self.pre_execute_fn = Some(Box::new(f));
321 }
322
323 pub fn set_execute_fn<F>(&mut self, f: F)
325 where
326 F: Fn(&mut RuleContext) -> RuleResult<()> + 'static,
327 {
328 self.execute_fn = Some(Box::new(f));
329 }
330
331 pub fn set_post_execute_fn<F>(&mut self, f: F)
333 where
334 F: Fn(&mut RuleContext) -> RuleResult<()> + 'static,
335 {
336 self.post_execute_fn = Some(Box::new(f));
337 }
338}
339
340impl Rule for BaseRule {
341 fn evaluate(&self, context: &RuleContext) -> RuleResult<bool> {
342 match &self.eval_fn {
343 Some(f) => f(context),
344 None => Ok(true), }
346 }
347
348 fn execute(&mut self, context: &mut RuleContext) -> RuleResult<()> {
349 if let Some(f) = &self.pre_execute_fn {
351 f(context)?;
352 }
353
354 if let Some(f) = &self.execute_fn {
356 f(context)?;
357 }
358
359 if let Some(f) = &self.post_execute_fn {
361 f(context)?;
362 }
363
364 Ok(())
365 }
366
367 fn children(&self) -> &[Box<dyn Rule>] {
368 &self.children
369 }
370
371 fn children_mut(&mut self) -> &mut Vec<Box<dyn Rule>> {
372 &mut self.children
373 }
374
375 fn add_child(&mut self, child: Box<dyn Rule>) -> RuleResult<()> {
376 self.children.push(child);
377 Ok(())
378 }
379}
380
381impl Default for BaseRule {
382 fn default() -> Self {
383 Self::new()
384 }
385}