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 log::info;
17use std::fs;
18use std::path::Path;
19
20#[cfg(feature = "streaming")]
21use crate::rete::network::{StreamWindowSpec, StreamWindowTypeRete};
22
23pub struct GrlReteLoader;
26
27impl GrlReteLoader {
28 pub fn load_from_file<P: AsRef<Path>>(
30 path: P,
31 engine: &mut IncrementalEngine,
32 ) -> Result<usize> {
33 let grl_text =
34 fs::read_to_string(path.as_ref()).map_err(|e| RuleEngineError::ParseError {
35 message: format!("Failed to read GRL file: {}", e),
36 })?;
37
38 Self::load_from_string(&grl_text, engine)
39 }
40
41 pub fn load_from_string(grl_text: &str, engine: &mut IncrementalEngine) -> Result<usize> {
43 let rules = GRLParser::parse_rules(grl_text)?;
45
46 let mut loaded_count = 0;
47
48 for rule in rules {
49 let rete_rule = Self::convert_rule_to_rete(rule)?;
51
52 let dependencies = Self::extract_dependencies(&rete_rule);
54
55 engine.add_rule(rete_rule, dependencies);
57 loaded_count += 1;
58 }
59
60 Ok(loaded_count)
61 }
62
63 fn convert_rule_to_rete(rule: Rule) -> Result<TypedReteUlRule> {
65 let node = Self::convert_condition_group(&rule.conditions)?;
67
68 let rete_rule = TypedReteUlRule {
70 name: rule.name.clone(),
71 node,
72 priority: rule.salience,
73 no_loop: rule.no_loop,
74 action: Self::create_action_closure(rule.actions),
75 };
76
77 Ok(rete_rule)
78 }
79
80 fn convert_condition_group(group: &ConditionGroup) -> Result<ReteUlNode> {
82 match group {
83 ConditionGroup::Single(condition) => Self::convert_condition(condition),
84 ConditionGroup::Compound {
85 left,
86 operator,
87 right,
88 } => {
89 let left_node = Self::convert_condition_group(left)?;
90 let right_node = Self::convert_condition_group(right)?;
91
92 match operator {
93 crate::types::LogicalOperator::And => {
94 Ok(ReteUlNode::UlAnd(Box::new(left_node), Box::new(right_node)))
95 }
96 crate::types::LogicalOperator::Or => {
97 Ok(ReteUlNode::UlOr(Box::new(left_node), Box::new(right_node)))
98 }
99 crate::types::LogicalOperator::Not => {
100 Ok(ReteUlNode::UlNot(Box::new(left_node)))
102 }
103 }
104 }
105 ConditionGroup::Not(inner) => {
106 let inner_node = Self::convert_condition_group(inner)?;
107 Ok(ReteUlNode::UlNot(Box::new(inner_node)))
108 }
109 ConditionGroup::Exists(inner) => {
110 let inner_node = Self::convert_condition_group(inner)?;
111 Ok(ReteUlNode::UlExists(Box::new(inner_node)))
112 }
113 ConditionGroup::Forall(inner) => {
114 let inner_node = Self::convert_condition_group(inner)?;
115 Ok(ReteUlNode::UlForall(Box::new(inner_node)))
116 }
117 ConditionGroup::Accumulate {
118 result_var,
119 source_pattern,
120 extract_field,
121 source_conditions,
122 function,
123 function_arg,
124 } => Ok(ReteUlNode::UlAccumulate {
125 result_var: result_var.clone(),
126 source_pattern: source_pattern.clone(),
127 extract_field: extract_field.clone(),
128 source_conditions: source_conditions.clone(),
129 function: function.clone(),
130 function_arg: function_arg.clone(),
131 }),
132 #[cfg(feature = "streaming")]
133 ConditionGroup::StreamPattern {
134 var_name,
135 event_type,
136 stream_name,
137 window,
138 } => {
139 Ok(ReteUlNode::UlStream {
141 var_name: var_name.clone(),
142 event_type: event_type.clone(),
143 stream_name: stream_name.clone(),
144 window: window.as_ref().map(|w| StreamWindowSpec {
145 duration: w.duration,
146 window_type: match &w.window_type {
147 crate::engine::rule::StreamWindowType::Sliding => {
148 StreamWindowTypeRete::Sliding
149 }
150 crate::engine::rule::StreamWindowType::Tumbling => {
151 StreamWindowTypeRete::Tumbling
152 }
153 crate::engine::rule::StreamWindowType::Session { timeout } => {
154 StreamWindowTypeRete::Session { timeout: *timeout }
155 }
156 },
157 }),
158 })
159 }
160 }
161 }
162
163 fn convert_condition(condition: &Condition) -> Result<ReteUlNode> {
165 use crate::engine::rule::ConditionExpression;
166
167 match &condition.expression {
169 ConditionExpression::MultiField {
170 field,
171 operation,
172 variable: _,
173 } => {
174 let operator_str = Self::operator_to_string(&condition.operator);
176 let value_str = if !matches!(condition.value, Value::Boolean(_)) {
177 Some(Self::value_to_string(&condition.value))
178 } else {
179 None
180 };
181
182 let (op, cmp_val) = if operation == "count" && operator_str != "==" {
184 (Some(operator_str), value_str)
186 } else {
187 (None, value_str)
189 };
190
191 Ok(ReteUlNode::UlMultiField {
192 field: field.clone(),
193 operation: operation.clone(),
194 value: if operation == "contains" {
195 cmp_val.clone()
196 } else {
197 None
198 },
199 operator: op,
200 compare_value: if operation == "count" { cmp_val } else { None },
201 })
202 }
203 ConditionExpression::FunctionCall { name, args } => {
204 Ok(ReteUlNode::UlFunctionCall {
205 name: name.clone(),
206 args: args.clone(),
207 operator: Self::operator_to_string(&condition.operator),
208 value: Self::value_to_string(&condition.value),
209 })
210 }
211 _ => {
212 let operator_str = Self::operator_to_string(&condition.operator);
214 let value_str = Self::value_to_string(&condition.value);
215
216 let alpha = AlphaNode {
217 field: condition.field.clone(),
218 operator: operator_str,
219 value: value_str,
220 };
221
222 Ok(ReteUlNode::UlAlpha(alpha))
223 }
224 }
225 }
226
227 fn operator_to_string(op: &Operator) -> String {
229 match op {
230 Operator::Equal => "==".to_string(),
231 Operator::NotEqual => "!=".to_string(),
232 Operator::GreaterThan => ">".to_string(),
233 Operator::GreaterThanOrEqual => ">=".to_string(),
234 Operator::LessThan => "<".to_string(),
235 Operator::LessThanOrEqual => "<=".to_string(),
236 Operator::Contains => "contains".to_string(),
237 Operator::NotContains => "!contains".to_string(),
238 Operator::StartsWith => "startsWith".to_string(),
239 Operator::EndsWith => "endsWith".to_string(),
240 Operator::Matches => "matches".to_string(),
241 Operator::In => "in".to_string(),
242 }
243 }
244
245 fn value_to_string(value: &Value) -> String {
247 match value {
248 Value::Number(n) => n.to_string(),
249 Value::Integer(i) => i.to_string(),
250 Value::String(s) => s.clone(),
251 Value::Boolean(b) => b.to_string(),
252 Value::Null => "null".to_string(),
253 Value::Array(arr) => {
254 let items: Vec<String> = arr.iter().map(Self::value_to_string).collect();
256 format!("[{}]", items.join(","))
257 }
258 Value::Object(_) => {
259 "object".to_string()
261 }
262 Value::Expression(expr) => {
263 expr.clone()
265 }
266 }
267 }
268
269 fn create_action_closure(
271 actions: Vec<crate::types::ActionType>,
272 ) -> std::sync::Arc<dyn Fn(&mut TypedFacts, &mut super::ActionResults) + Send + Sync> {
273 std::sync::Arc::new(
274 move |facts: &mut TypedFacts, results: &mut super::ActionResults| {
275 for action in &actions {
277 Self::execute_action(action, facts, results);
278 }
279 },
280 )
281 }
282
283 fn execute_action(
285 action: &crate::types::ActionType,
286 facts: &mut TypedFacts,
287 results: &mut super::ActionResults,
288 ) {
289 use crate::types::ActionType;
290
291 match action {
292 ActionType::Set { field, value } => {
293 let evaluated_value = match value {
299 Value::Expression(expr) => {
300 Self::evaluate_expression_for_rete(expr, facts)
302 }
303 _ => value.clone(),
304 };
305
306 let fact_value = Self::value_to_fact_value(&evaluated_value);
308 facts.set(field, fact_value);
309 }
310 ActionType::Log { message } => {
311 info!("📝 {}", message);
312 }
313 ActionType::MethodCall {
314 object,
315 method,
316 args,
317 } => {
318 let mut all_args = vec![object.clone()];
320 all_args.extend(args.iter().map(Self::value_to_string));
321
322 results.add(super::ActionResult::CallFunction {
323 function_name: format!("{}.{}", object, method),
324 args: all_args,
325 });
326 println!("� METHOD: {}.{}", object, method);
327 }
328 ActionType::Retract { object } => {
329 let object_name = object.trim_matches('"');
331
332 if let Some(handle) = facts.get_fact_handle(object_name) {
334 results.add(super::ActionResult::Retract(handle));
336 println!("🗑️ RETRACT: {} (handle: {:?})", object_name, handle);
337 } else {
338 results.add(super::ActionResult::RetractByType(object_name.to_string()));
340 println!("🗑️ RETRACT: {} (by type, no handle found)", object_name);
341 }
342 }
343 ActionType::Custom {
344 action_type,
345 params,
346 } => {
347 let args: Vec<String> = params.values().map(Self::value_to_string).collect();
349
350 results.add(super::ActionResult::CallFunction {
351 function_name: action_type.clone(),
352 args,
353 });
354 println!("🔧 CUSTOM CALL: {}", action_type);
355 }
356 ActionType::ActivateAgendaGroup { group } => {
357 results.add(super::ActionResult::ActivateAgendaGroup(group.clone()));
359 println!("📋 ACTIVATE GROUP: {}", group);
360 }
361 ActionType::ScheduleRule {
362 rule_name,
363 delay_ms,
364 } => {
365 results.add(super::ActionResult::ScheduleRule {
367 rule_name: rule_name.clone(),
368 delay_ms: *delay_ms,
369 });
370 println!("⏰ SCHEDULE: {} (delay: {}ms)", rule_name, delay_ms);
371 }
372 ActionType::CompleteWorkflow { workflow_name } => {
373 let completion_key = format!("workflow.{}.completed", workflow_name);
375 facts.set(&completion_key, FactValue::Boolean(true));
376
377 let timestamp_key = format!("workflow.{}.completed_at", workflow_name);
378 facts.set(
379 ×tamp_key,
380 FactValue::Integer(chrono::Utc::now().timestamp()),
381 );
382
383 println!("✔️ WORKFLOW COMPLETED: {}", workflow_name);
384 }
385 ActionType::SetWorkflowData { key, value } => {
386 let data_key = format!("workflow.data.{}", key);
388 let fact_value = Self::value_to_fact_value(value);
389 facts.set(&data_key, fact_value);
390
391 println!("📊 WORKFLOW DATA SET: {} = {:?}", key, value);
392 }
393 ActionType::Append { field, value } => {
394 let current_value = facts.get(field);
397
398 let mut array = match current_value {
399 Some(FactValue::Array(arr)) => arr.clone(),
400 Some(_) => {
401 log::warn!("Field {} is not an array, creating new array", field);
403 Vec::new()
404 }
405 None => {
406 Vec::new()
408 }
409 };
410
411 let evaluated_value = match value {
413 Value::Expression(expr) => Self::evaluate_expression_for_rete(expr, facts),
414 _ => value.clone(),
415 };
416
417 let fact_value = Self::value_to_fact_value(&evaluated_value);
419 array.push(fact_value);
420
421 facts.set(field, FactValue::Array(array));
423
424 info!("➕ APPEND: {} += {:?}", field, evaluated_value);
425 }
426 }
427 }
428
429 fn value_to_fact_value(value: &Value) -> FactValue {
431 match value {
432 Value::Number(n) => {
433 if n.fract() == 0.0 {
435 FactValue::Integer(*n as i64)
436 } else {
437 FactValue::Float(*n)
438 }
439 }
440 Value::Integer(i) => FactValue::Integer(*i),
441 Value::String(s) => FactValue::String(s.clone()),
442 Value::Boolean(b) => FactValue::Boolean(*b),
443 Value::Null => FactValue::Null,
444 Value::Array(arr) => {
445 let fact_arr: Vec<FactValue> = arr.iter().map(Self::value_to_fact_value).collect();
446 FactValue::Array(fact_arr)
447 }
448 Value::Object(_) => {
449 FactValue::String("object".to_string())
451 }
452 Value::Expression(expr) => {
453 FactValue::String(format!("[EXPR: {}]", expr))
455 }
456 }
457 }
458
459 fn extract_dependencies(rule: &TypedReteUlRule) -> Vec<String> {
461 let mut deps = Vec::new();
462 Self::extract_deps_from_node(&rule.node, &mut deps);
463
464 deps.sort();
466 deps.dedup();
467
468 deps
469 }
470
471 fn extract_deps_from_node(node: &ReteUlNode, deps: &mut Vec<String>) {
473 match node {
474 ReteUlNode::UlAlpha(alpha) => {
475 if let Some(dot_pos) = alpha.field.find('.') {
477 let fact_type = alpha.field[..dot_pos].to_string();
478 deps.push(fact_type);
479 }
480 }
481 ReteUlNode::UlMultiField { field, .. } => {
482 if let Some(dot_pos) = field.find('.') {
484 let fact_type = field[..dot_pos].to_string();
485 deps.push(fact_type);
486 }
487 }
488 ReteUlNode::UlAnd(left, right) | ReteUlNode::UlOr(left, right) => {
489 Self::extract_deps_from_node(left, deps);
490 Self::extract_deps_from_node(right, deps);
491 }
492 ReteUlNode::UlNot(inner)
493 | ReteUlNode::UlExists(inner)
494 | ReteUlNode::UlForall(inner) => {
495 Self::extract_deps_from_node(inner, deps);
496 }
497 ReteUlNode::UlAccumulate { source_pattern, .. } => {
498 deps.push(source_pattern.clone());
500 }
501 #[cfg(feature = "streaming")]
502 ReteUlNode::UlStream { stream_name, .. } => {
503 deps.push(stream_name.clone());
505 }
506 ReteUlNode::UlFunctionCall { args, .. } => {
507 for arg in args {
508 if let Some(dot_pos) = arg.find('.') {
509 deps.push(arg[..dot_pos].to_string());
510 }
511 }
512 }
513 ReteUlNode::UlTerminal(_) => {}
514 }
515 }
516
517 fn evaluate_expression_for_rete(expr: &str, typed_facts: &TypedFacts) -> Value {
519 use crate::engine::facts::Facts;
521
522 let facts = Facts::new();
523
524 for (key, value) in typed_facts.get_all() {
528 let converted_value = Self::fact_value_to_value(value);
529
530 facts.set(key, converted_value.clone());
533
534 if !key.contains('.') {
536 facts.set(&format!("Order.{}", key), converted_value);
537 }
538 }
539
540 match crate::expression::evaluate_expression(expr, &facts) {
542 Ok(result) => result,
543 Err(_e) => {
544 Value::String(expr.to_string())
547 }
548 }
549 }
550
551 fn fact_value_to_value(fact_value: &FactValue) -> Value {
553 match fact_value {
554 FactValue::String(s) => {
555 if let Ok(i) = s.parse::<i64>() {
557 Value::Integer(i)
558 } else if let Ok(f) = s.parse::<f64>() {
559 Value::Number(f)
560 } else if s == "true" {
561 Value::Boolean(true)
562 } else if s == "false" {
563 Value::Boolean(false)
564 } else {
565 Value::String(s.clone())
566 }
567 }
568 FactValue::Integer(i) => Value::Integer(*i),
569 FactValue::Float(f) => Value::Number(*f),
570 FactValue::Boolean(b) => Value::Boolean(*b),
571 FactValue::Array(arr) => {
572 Value::Array(arr.iter().map(Self::fact_value_to_value).collect())
573 }
574 FactValue::Null => Value::Null,
575 }
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582
583 #[test]
584 fn test_convert_simple_rule() {
585 let grl = r#"
586 rule "TestRule" salience 10 no-loop {
587 when
588 Person.age > 18
589 then
590 Person.is_adult = true;
591 }
592 "#;
593
594 let rules = GRLParser::parse_rules(grl).unwrap();
595 assert_eq!(rules.len(), 1);
596
597 let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
598 assert_eq!(rete_rule.name, "TestRule");
599 assert_eq!(rete_rule.priority, 10);
600 assert!(rete_rule.no_loop);
601 }
602
603 #[test]
604 fn test_extract_dependencies() {
605 let grl = r#"
606 rule "MultiTypeRule" {
607 when
608 Person.age > 18 && Order.amount > 1000
609 then
610 Person.premium = true;
611 }
612 "#;
613
614 let rules = GRLParser::parse_rules(grl).unwrap();
615 let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
616 let deps = GrlReteLoader::extract_dependencies(&rete_rule);
617
618 assert_eq!(deps.len(), 2);
619 assert!(deps.contains(&"Person".to_string()));
620 assert!(deps.contains(&"Order".to_string()));
621 }
622
623 #[test]
624 fn test_load_from_string() {
625 let grl = r#"
626 rule "Rule1" {
627 when
628 Person.age > 18
629 then
630 Person.is_adult = true;
631 }
632
633 rule "Rule2" {
634 when
635 Order.amount > 1000
636 then
637 Order.high_value = true;
638 }
639 "#;
640
641 let mut engine = IncrementalEngine::new();
642 let count = GrlReteLoader::load_from_string(grl, &mut engine).unwrap();
643
644 assert_eq!(count, 2);
645 }
646}