1#![allow(clippy::type_complexity)]
7#![allow(deprecated)]
8
9use crate::engine::rule::{Condition, ConditionGroup, Rule};
10use crate::errors::{Result, RuleEngineError};
11use crate::parser::GRLParser;
12use crate::rete::facts::{FactValue, TypedFacts};
13use crate::rete::propagation::IncrementalEngine;
14use crate::rete::{AlphaNode, ReteUlNode, TypedReteUlRule};
15use crate::types::{Operator, Value};
16use std::fs;
17use std::path::Path;
18
19pub struct GrlReteLoader;
22
23impl GrlReteLoader {
24 pub fn load_from_file<P: AsRef<Path>>(
26 path: P,
27 engine: &mut IncrementalEngine,
28 ) -> Result<usize> {
29 let grl_text =
30 fs::read_to_string(path.as_ref()).map_err(|e| RuleEngineError::ParseError {
31 message: format!("Failed to read GRL file: {}", e),
32 })?;
33
34 Self::load_from_string(&grl_text, engine)
35 }
36
37 pub fn load_from_string(grl_text: &str, engine: &mut IncrementalEngine) -> Result<usize> {
39 let rules = GRLParser::parse_rules(grl_text)?;
41
42 let mut loaded_count = 0;
43
44 for rule in rules {
45 let rete_rule = Self::convert_rule_to_rete(rule)?;
47
48 let dependencies = Self::extract_dependencies(&rete_rule);
50
51 engine.add_rule(rete_rule, dependencies);
53 loaded_count += 1;
54 }
55
56 Ok(loaded_count)
57 }
58
59 fn convert_rule_to_rete(rule: Rule) -> Result<TypedReteUlRule> {
61 let node = Self::convert_condition_group(&rule.conditions)?;
63
64 let rete_rule = TypedReteUlRule {
66 name: rule.name.clone(),
67 node,
68 priority: rule.salience,
69 no_loop: rule.no_loop,
70 action: Self::create_action_closure(rule.actions),
71 };
72
73 Ok(rete_rule)
74 }
75
76 fn convert_condition_group(group: &ConditionGroup) -> Result<ReteUlNode> {
78 match group {
79 ConditionGroup::Single(condition) => Self::convert_condition(condition),
80 ConditionGroup::Compound {
81 left,
82 operator,
83 right,
84 } => {
85 let left_node = Self::convert_condition_group(left)?;
86 let right_node = Self::convert_condition_group(right)?;
87
88 match operator {
89 crate::types::LogicalOperator::And => {
90 Ok(ReteUlNode::UlAnd(Box::new(left_node), Box::new(right_node)))
91 }
92 crate::types::LogicalOperator::Or => {
93 Ok(ReteUlNode::UlOr(Box::new(left_node), Box::new(right_node)))
94 }
95 crate::types::LogicalOperator::Not => {
96 Ok(ReteUlNode::UlNot(Box::new(left_node)))
98 }
99 }
100 }
101 ConditionGroup::Not(inner) => {
102 let inner_node = Self::convert_condition_group(inner)?;
103 Ok(ReteUlNode::UlNot(Box::new(inner_node)))
104 }
105 ConditionGroup::Exists(inner) => {
106 let inner_node = Self::convert_condition_group(inner)?;
107 Ok(ReteUlNode::UlExists(Box::new(inner_node)))
108 }
109 ConditionGroup::Forall(inner) => {
110 let inner_node = Self::convert_condition_group(inner)?;
111 Ok(ReteUlNode::UlForall(Box::new(inner_node)))
112 }
113 ConditionGroup::Accumulate {
114 result_var,
115 source_pattern,
116 extract_field,
117 source_conditions,
118 function,
119 function_arg,
120 } => Ok(ReteUlNode::UlAccumulate {
121 result_var: result_var.clone(),
122 source_pattern: source_pattern.clone(),
123 extract_field: extract_field.clone(),
124 source_conditions: source_conditions.clone(),
125 function: function.clone(),
126 function_arg: function_arg.clone(),
127 }),
128 }
129 }
130
131 fn convert_condition(condition: &Condition) -> Result<ReteUlNode> {
133 use crate::engine::rule::ConditionExpression;
134
135 match &condition.expression {
137 ConditionExpression::MultiField {
138 field,
139 operation,
140 variable: _,
141 } => {
142 let operator_str = Self::operator_to_string(&condition.operator);
144 let value_str = if !matches!(condition.value, Value::Boolean(_)) {
145 Some(Self::value_to_string(&condition.value))
146 } else {
147 None
148 };
149
150 let (op, cmp_val) = if operation == "count" && operator_str != "==" {
152 (Some(operator_str), value_str)
154 } else {
155 (None, value_str)
157 };
158
159 Ok(ReteUlNode::UlMultiField {
160 field: field.clone(),
161 operation: operation.clone(),
162 value: if operation == "contains" {
163 cmp_val.clone()
164 } else {
165 None
166 },
167 operator: op,
168 compare_value: if operation == "count" { cmp_val } else { None },
169 })
170 }
171 _ => {
172 let operator_str = Self::operator_to_string(&condition.operator);
174 let value_str = Self::value_to_string(&condition.value);
175
176 let alpha = AlphaNode {
177 field: condition.field.clone(),
178 operator: operator_str,
179 value: value_str,
180 };
181
182 Ok(ReteUlNode::UlAlpha(alpha))
183 }
184 }
185 }
186
187 fn operator_to_string(op: &Operator) -> String {
189 match op {
190 Operator::Equal => "==".to_string(),
191 Operator::NotEqual => "!=".to_string(),
192 Operator::GreaterThan => ">".to_string(),
193 Operator::GreaterThanOrEqual => ">=".to_string(),
194 Operator::LessThan => "<".to_string(),
195 Operator::LessThanOrEqual => "<=".to_string(),
196 Operator::Contains => "contains".to_string(),
197 Operator::NotContains => "!contains".to_string(),
198 Operator::StartsWith => "startsWith".to_string(),
199 Operator::EndsWith => "endsWith".to_string(),
200 Operator::Matches => "matches".to_string(),
201 }
202 }
203
204 fn value_to_string(value: &Value) -> String {
206 match value {
207 Value::Number(n) => n.to_string(),
208 Value::Integer(i) => i.to_string(),
209 Value::String(s) => s.clone(),
210 Value::Boolean(b) => b.to_string(),
211 Value::Null => "null".to_string(),
212 Value::Array(arr) => {
213 let items: Vec<String> = arr.iter().map(Self::value_to_string).collect();
215 format!("[{}]", items.join(","))
216 }
217 Value::Object(_) => {
218 "object".to_string()
220 }
221 Value::Expression(expr) => {
222 expr.clone()
224 }
225 }
226 }
227
228 fn create_action_closure(
230 actions: Vec<crate::types::ActionType>,
231 ) -> std::sync::Arc<dyn Fn(&mut TypedFacts, &mut super::ActionResults) + Send + Sync> {
232 std::sync::Arc::new(
233 move |facts: &mut TypedFacts, results: &mut super::ActionResults| {
234 for action in &actions {
236 Self::execute_action(action, facts, results);
237 }
238 },
239 )
240 }
241
242 fn execute_action(
244 action: &crate::types::ActionType,
245 facts: &mut TypedFacts,
246 results: &mut super::ActionResults,
247 ) {
248 use crate::types::ActionType;
249
250 match action {
251 ActionType::Set { field, value } => {
252 let evaluated_value = match value {
258 Value::Expression(expr) => {
259 Self::evaluate_expression_for_rete(expr, facts)
261 }
262 _ => value.clone(),
263 };
264
265 let fact_value = Self::value_to_fact_value(&evaluated_value);
267 facts.set(field, fact_value);
268 }
269 ActionType::Log { message } => {
270 println!("📝 LOG: {}", message);
271 }
272 ActionType::MethodCall {
273 object,
274 method,
275 args,
276 } => {
277 let mut all_args = vec![object.clone()];
279 all_args.extend(args.iter().map(Self::value_to_string));
280
281 results.add(super::ActionResult::CallFunction {
282 function_name: format!("{}.{}", object, method),
283 args: all_args,
284 });
285 println!("� METHOD: {}.{}", object, method);
286 }
287 ActionType::Retract { object } => {
288 let object_name = object.trim_matches('"');
290
291 if let Some(handle) = facts.get_fact_handle(object_name) {
293 results.add(super::ActionResult::Retract(handle));
295 println!("🗑️ RETRACT: {} (handle: {:?})", object_name, handle);
296 } else {
297 results.add(super::ActionResult::RetractByType(object_name.to_string()));
299 println!("🗑️ RETRACT: {} (by type, no handle found)", object_name);
300 }
301 }
302 ActionType::Custom {
303 action_type,
304 params,
305 } => {
306 let args: Vec<String> = params.values().map(Self::value_to_string).collect();
308
309 results.add(super::ActionResult::CallFunction {
310 function_name: action_type.clone(),
311 args,
312 });
313 println!("🔧 CUSTOM CALL: {}", action_type);
314 }
315 ActionType::ActivateAgendaGroup { group } => {
316 results.add(super::ActionResult::ActivateAgendaGroup(group.clone()));
318 println!("📋 ACTIVATE GROUP: {}", group);
319 }
320 ActionType::ScheduleRule {
321 rule_name,
322 delay_ms,
323 } => {
324 results.add(super::ActionResult::ScheduleRule {
326 rule_name: rule_name.clone(),
327 delay_ms: *delay_ms,
328 });
329 println!("⏰ SCHEDULE: {} (delay: {}ms)", rule_name, delay_ms);
330 }
331 ActionType::CompleteWorkflow { workflow_name } => {
332 let completion_key = format!("workflow.{}.completed", workflow_name);
334 facts.set(&completion_key, FactValue::Boolean(true));
335
336 let timestamp_key = format!("workflow.{}.completed_at", workflow_name);
337 facts.set(
338 ×tamp_key,
339 FactValue::Integer(chrono::Utc::now().timestamp()),
340 );
341
342 println!("✔️ WORKFLOW COMPLETED: {}", workflow_name);
343 }
344 ActionType::SetWorkflowData { key, value } => {
345 let data_key = format!("workflow.data.{}", key);
347 let fact_value = Self::value_to_fact_value(value);
348 facts.set(&data_key, fact_value);
349
350 println!("📊 WORKFLOW DATA SET: {} = {:?}", key, value);
351 }
352 }
353 }
354
355 fn value_to_fact_value(value: &Value) -> FactValue {
357 match value {
358 Value::Number(n) => {
359 if n.fract() == 0.0 {
361 FactValue::Integer(*n as i64)
362 } else {
363 FactValue::Float(*n)
364 }
365 }
366 Value::Integer(i) => FactValue::Integer(*i),
367 Value::String(s) => FactValue::String(s.clone()),
368 Value::Boolean(b) => FactValue::Boolean(*b),
369 Value::Null => FactValue::Null,
370 Value::Array(arr) => {
371 let fact_arr: Vec<FactValue> = arr.iter().map(Self::value_to_fact_value).collect();
372 FactValue::Array(fact_arr)
373 }
374 Value::Object(_) => {
375 FactValue::String("object".to_string())
377 }
378 Value::Expression(expr) => {
379 FactValue::String(format!("[EXPR: {}]", expr))
381 }
382 }
383 }
384
385 fn extract_dependencies(rule: &TypedReteUlRule) -> Vec<String> {
387 let mut deps = Vec::new();
388 Self::extract_deps_from_node(&rule.node, &mut deps);
389
390 deps.sort();
392 deps.dedup();
393
394 deps
395 }
396
397 fn extract_deps_from_node(node: &ReteUlNode, deps: &mut Vec<String>) {
399 match node {
400 ReteUlNode::UlAlpha(alpha) => {
401 if let Some(dot_pos) = alpha.field.find('.') {
403 let fact_type = alpha.field[..dot_pos].to_string();
404 deps.push(fact_type);
405 }
406 }
407 ReteUlNode::UlMultiField { field, .. } => {
408 if let Some(dot_pos) = field.find('.') {
410 let fact_type = field[..dot_pos].to_string();
411 deps.push(fact_type);
412 }
413 }
414 ReteUlNode::UlAnd(left, right) | ReteUlNode::UlOr(left, right) => {
415 Self::extract_deps_from_node(left, deps);
416 Self::extract_deps_from_node(right, deps);
417 }
418 ReteUlNode::UlNot(inner)
419 | ReteUlNode::UlExists(inner)
420 | ReteUlNode::UlForall(inner) => {
421 Self::extract_deps_from_node(inner, deps);
422 }
423 ReteUlNode::UlAccumulate { source_pattern, .. } => {
424 deps.push(source_pattern.clone());
426 }
427 ReteUlNode::UlTerminal(_) => {
428 }
430 }
431 }
432
433 fn evaluate_expression_for_rete(expr: &str, typed_facts: &TypedFacts) -> Value {
435 use crate::engine::facts::Facts;
437
438 let facts = Facts::new();
439
440 for (key, value) in typed_facts.get_all() {
444 let converted_value = Self::fact_value_to_value(value);
445
446 facts.set(key, converted_value.clone());
449
450 if !key.contains('.') {
452 facts.set(&format!("Order.{}", key), converted_value);
453 }
454 }
455
456 match crate::expression::evaluate_expression(expr, &facts) {
458 Ok(result) => result,
459 Err(_e) => {
460 Value::String(expr.to_string())
463 }
464 }
465 }
466
467 fn fact_value_to_value(fact_value: &FactValue) -> Value {
469 match fact_value {
470 FactValue::String(s) => {
471 if let Ok(i) = s.parse::<i64>() {
473 Value::Integer(i)
474 } else if let Ok(f) = s.parse::<f64>() {
475 Value::Number(f)
476 } else if s == "true" {
477 Value::Boolean(true)
478 } else if s == "false" {
479 Value::Boolean(false)
480 } else {
481 Value::String(s.clone())
482 }
483 }
484 FactValue::Integer(i) => Value::Integer(*i),
485 FactValue::Float(f) => Value::Number(*f),
486 FactValue::Boolean(b) => Value::Boolean(*b),
487 FactValue::Array(arr) => {
488 Value::Array(arr.iter().map(Self::fact_value_to_value).collect())
489 }
490 FactValue::Null => Value::Null,
491 }
492 }
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 #[test]
500 fn test_convert_simple_rule() {
501 let grl = r#"
502 rule "TestRule" salience 10 no-loop {
503 when
504 Person.age > 18
505 then
506 Person.is_adult = true;
507 }
508 "#;
509
510 let rules = GRLParser::parse_rules(grl).unwrap();
511 assert_eq!(rules.len(), 1);
512
513 let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
514 assert_eq!(rete_rule.name, "TestRule");
515 assert_eq!(rete_rule.priority, 10);
516 assert!(rete_rule.no_loop);
517 }
518
519 #[test]
520 fn test_extract_dependencies() {
521 let grl = r#"
522 rule "MultiTypeRule" {
523 when
524 Person.age > 18 && Order.amount > 1000
525 then
526 Person.premium = true;
527 }
528 "#;
529
530 let rules = GRLParser::parse_rules(grl).unwrap();
531 let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
532 let deps = GrlReteLoader::extract_dependencies(&rete_rule);
533
534 assert_eq!(deps.len(), 2);
535 assert!(deps.contains(&"Person".to_string()));
536 assert!(deps.contains(&"Order".to_string()));
537 }
538
539 #[test]
540 fn test_load_from_string() {
541 let grl = r#"
542 rule "Rule1" {
543 when
544 Person.age > 18
545 then
546 Person.is_adult = true;
547 }
548
549 rule "Rule2" {
550 when
551 Order.amount > 1000
552 then
553 Order.high_value = true;
554 }
555 "#;
556
557 let mut engine = IncrementalEngine::new();
558 let count = GrlReteLoader::load_from_string(grl, &mut engine).unwrap();
559
560 assert_eq!(count, 2);
561 }
562}