rust_rule_engine/rete/
grl_loader.rs1use crate::engine::rule::{Condition, ConditionGroup, Rule};
7use crate::parser::GRLParser;
8use crate::rete::{AlphaNode, ReteUlNode, TypedReteUlRule};
9use crate::rete::facts::{TypedFacts, FactValue};
10use crate::rete::propagation::IncrementalEngine;
11use crate::types::{Operator, Value};
12use crate::errors::{Result, RuleEngineError};
13use std::fs;
14use std::path::Path;
15
16pub struct GrlReteLoader;
19
20impl GrlReteLoader {
21 pub fn load_from_file<P: AsRef<Path>>(
23 path: P,
24 engine: &mut IncrementalEngine,
25 ) -> Result<usize> {
26 let grl_text = fs::read_to_string(path.as_ref()).map_err(|e| {
27 RuleEngineError::ParseError {
28 message: format!("Failed to read GRL file: {}", e),
29 }
30 })?;
31
32 Self::load_from_string(&grl_text, engine)
33 }
34
35 pub fn load_from_string(
37 grl_text: &str,
38 engine: &mut IncrementalEngine,
39 ) -> Result<usize> {
40 let rules = GRLParser::parse_rules(grl_text)?;
42
43 let mut loaded_count = 0;
44
45 for rule in rules {
46 let rete_rule = Self::convert_rule_to_rete(rule)?;
48
49 let dependencies = Self::extract_dependencies(&rete_rule);
51
52 engine.add_rule(rete_rule, dependencies);
54 loaded_count += 1;
55 }
56
57 Ok(loaded_count)
58 }
59
60 fn convert_rule_to_rete(rule: Rule) -> Result<TypedReteUlRule> {
62 let node = Self::convert_condition_group(&rule.conditions)?;
64
65 let rete_rule = TypedReteUlRule {
67 name: rule.name.clone(),
68 node,
69 priority: rule.salience,
70 no_loop: rule.no_loop,
71 action: Self::create_action_closure(rule.actions),
72 };
73
74 Ok(rete_rule)
75 }
76
77 fn convert_condition_group(group: &ConditionGroup) -> Result<ReteUlNode> {
79 match group {
80 ConditionGroup::Single(condition) => {
81 Self::convert_condition(condition)
82 }
83 ConditionGroup::Compound { left, operator, right } => {
84 let left_node = Self::convert_condition_group(left)?;
85 let right_node = Self::convert_condition_group(right)?;
86
87 match operator {
88 crate::types::LogicalOperator::And => {
89 Ok(ReteUlNode::UlAnd(Box::new(left_node), Box::new(right_node)))
90 }
91 crate::types::LogicalOperator::Or => {
92 Ok(ReteUlNode::UlOr(Box::new(left_node), Box::new(right_node)))
93 }
94 crate::types::LogicalOperator::Not => {
95 Ok(ReteUlNode::UlNot(Box::new(left_node)))
97 }
98 }
99 }
100 ConditionGroup::Not(inner) => {
101 let inner_node = Self::convert_condition_group(inner)?;
102 Ok(ReteUlNode::UlNot(Box::new(inner_node)))
103 }
104 ConditionGroup::Exists(inner) => {
105 let inner_node = Self::convert_condition_group(inner)?;
106 Ok(ReteUlNode::UlExists(Box::new(inner_node)))
107 }
108 ConditionGroup::Forall(inner) => {
109 let inner_node = Self::convert_condition_group(inner)?;
110 Ok(ReteUlNode::UlForall(Box::new(inner_node)))
111 }
112 }
113 }
114
115 fn convert_condition(condition: &Condition) -> Result<ReteUlNode> {
117 let operator_str = Self::operator_to_string(&condition.operator);
118 let value_str = Self::value_to_string(&condition.value);
119
120 let alpha = AlphaNode {
121 field: condition.field.clone(),
122 operator: operator_str,
123 value: value_str,
124 };
125
126 Ok(ReteUlNode::UlAlpha(alpha))
127 }
128
129 fn operator_to_string(op: &Operator) -> String {
131 match op {
132 Operator::Equal => "==".to_string(),
133 Operator::NotEqual => "!=".to_string(),
134 Operator::GreaterThan => ">".to_string(),
135 Operator::GreaterThanOrEqual => ">=".to_string(),
136 Operator::LessThan => "<".to_string(),
137 Operator::LessThanOrEqual => "<=".to_string(),
138 Operator::Contains => "contains".to_string(),
139 Operator::NotContains => "!contains".to_string(),
140 Operator::StartsWith => "startsWith".to_string(),
141 Operator::EndsWith => "endsWith".to_string(),
142 Operator::Matches => "matches".to_string(),
143 }
144 }
145
146 fn value_to_string(value: &Value) -> String {
148 match value {
149 Value::Number(n) => n.to_string(),
150 Value::Integer(i) => i.to_string(),
151 Value::String(s) => s.clone(),
152 Value::Boolean(b) => b.to_string(),
153 Value::Null => "null".to_string(),
154 Value::Array(arr) => {
155 let items: Vec<String> = arr.iter()
157 .map(|v| Self::value_to_string(v))
158 .collect();
159 format!("[{}]", items.join(","))
160 }
161 Value::Object(_) => {
162 "object".to_string()
164 }
165 }
166 }
167
168 fn create_action_closure(
170 actions: Vec<crate::types::ActionType>,
171 ) -> Box<dyn FnMut(&mut TypedFacts)> {
172 Box::new(move |facts: &mut TypedFacts| {
173 for action in &actions {
175 Self::execute_action(action, facts);
176 }
177 })
178 }
179
180 fn execute_action(action: &crate::types::ActionType, facts: &mut TypedFacts) {
182 use crate::types::ActionType;
183
184 match action {
185 ActionType::Set { field, value } => {
186 let fact_value = Self::value_to_fact_value(value);
188 facts.set(field, fact_value);
189 }
190 ActionType::Log { message } => {
191 println!("📝 LOG: {}", message);
192 }
193 ActionType::Call { function, args: _ } => {
194 println!("🔧 CALL: {}", function);
196 }
197 ActionType::MethodCall { object, method, args: _ } => {
198 println!("📞 METHOD: {}.{}", object, method);
199 }
200 ActionType::Update { object } => {
201 println!("🔄 UPDATE: {}", object);
202 }
203 ActionType::Custom { action_type, params: _ } => {
204 println!("⚙️ CUSTOM: {}", action_type);
205 }
206 ActionType::ActivateAgendaGroup { group } => {
207 println!("📋 ACTIVATE GROUP: {}", group);
208 }
209 ActionType::ScheduleRule { rule_name, delay_ms } => {
210 println!("⏰ SCHEDULE: {} (delay: {}ms)", rule_name, delay_ms);
211 }
212 ActionType::CompleteWorkflow { workflow_name } => {
213 println!("✔️ COMPLETE WORKFLOW: {}", workflow_name);
214 }
215 ActionType::SetWorkflowData { key, value: _ } => {
216 println!("📊 SET WORKFLOW DATA: {}", key);
217 }
218 }
219 }
220
221 fn value_to_fact_value(value: &Value) -> FactValue {
223 match value {
224 Value::Number(n) => {
225 if n.fract() == 0.0 {
227 FactValue::Integer(*n as i64)
228 } else {
229 FactValue::Float(*n)
230 }
231 }
232 Value::Integer(i) => FactValue::Integer(*i),
233 Value::String(s) => FactValue::String(s.clone()),
234 Value::Boolean(b) => FactValue::Boolean(*b),
235 Value::Null => FactValue::Null,
236 Value::Array(arr) => {
237 let fact_arr: Vec<FactValue> = arr.iter()
238 .map(Self::value_to_fact_value)
239 .collect();
240 FactValue::Array(fact_arr)
241 }
242 Value::Object(_) => {
243 FactValue::String("object".to_string())
245 }
246 }
247 }
248
249 fn extract_dependencies(rule: &TypedReteUlRule) -> Vec<String> {
251 let mut deps = Vec::new();
252 Self::extract_deps_from_node(&rule.node, &mut deps);
253
254 deps.sort();
256 deps.dedup();
257
258 deps
259 }
260
261 fn extract_deps_from_node(node: &ReteUlNode, deps: &mut Vec<String>) {
263 match node {
264 ReteUlNode::UlAlpha(alpha) => {
265 if let Some(dot_pos) = alpha.field.find('.') {
267 let fact_type = alpha.field[..dot_pos].to_string();
268 deps.push(fact_type);
269 }
270 }
271 ReteUlNode::UlAnd(left, right) | ReteUlNode::UlOr(left, right) => {
272 Self::extract_deps_from_node(left, deps);
273 Self::extract_deps_from_node(right, deps);
274 }
275 ReteUlNode::UlNot(inner)
276 | ReteUlNode::UlExists(inner)
277 | ReteUlNode::UlForall(inner) => {
278 Self::extract_deps_from_node(inner, deps);
279 }
280 ReteUlNode::UlTerminal(_) => {
281 }
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_convert_simple_rule() {
293 let grl = r#"
294 rule "TestRule" salience 10 no-loop {
295 when
296 Person.age > 18
297 then
298 Person.is_adult = true;
299 }
300 "#;
301
302 let rules = GRLParser::parse_rules(grl).unwrap();
303 assert_eq!(rules.len(), 1);
304
305 let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
306 assert_eq!(rete_rule.name, "TestRule");
307 assert_eq!(rete_rule.priority, 10);
308 assert!(rete_rule.no_loop);
309 }
310
311 #[test]
312 fn test_extract_dependencies() {
313 let grl = r#"
314 rule "MultiTypeRule" {
315 when
316 Person.age > 18 && Order.amount > 1000
317 then
318 Person.premium = true;
319 }
320 "#;
321
322 let rules = GRLParser::parse_rules(grl).unwrap();
323 let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
324 let deps = GrlReteLoader::extract_dependencies(&rete_rule);
325
326 assert_eq!(deps.len(), 2);
327 assert!(deps.contains(&"Person".to_string()));
328 assert!(deps.contains(&"Order".to_string()));
329 }
330
331 #[test]
332 fn test_load_from_string() {
333 let grl = r#"
334 rule "Rule1" {
335 when
336 Person.age > 18
337 then
338 Person.is_adult = true;
339 }
340
341 rule "Rule2" {
342 when
343 Order.amount > 1000
344 then
345 Order.high_value = true;
346 }
347 "#;
348
349 let mut engine = IncrementalEngine::new();
350 let count = GrlReteLoader::load_from_string(grl, &mut engine).unwrap();
351
352 assert_eq!(count, 2);
353 }
354}