rust_rule_engine/rete/
grl_loader.rs

1//! GRL to RETE Converter
2//!
3//! This module converts GRL (Grule Rule Language) rules into RETE-UL structures
4//! for efficient pattern matching and rule execution.
5
6#![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
19/// GRL to RETE Loader
20/// Converts GRL rules into RETE-UL structures
21pub struct GrlReteLoader;
22
23impl GrlReteLoader {
24    /// Load rules from a GRL file into RETE engine
25    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    /// Load rules from GRL string into RETE engine
38    pub fn load_from_string(grl_text: &str, engine: &mut IncrementalEngine) -> Result<usize> {
39        // Parse GRL rules
40        let rules = GRLParser::parse_rules(grl_text)?;
41
42        let mut loaded_count = 0;
43
44        for rule in rules {
45            // Convert GRL rule to RETE rule
46            let rete_rule = Self::convert_rule_to_rete(rule)?;
47
48            // Extract dependencies (fact types used in conditions)
49            let dependencies = Self::extract_dependencies(&rete_rule);
50
51            // Add to engine
52            engine.add_rule(rete_rule, dependencies);
53            loaded_count += 1;
54        }
55
56        Ok(loaded_count)
57    }
58
59    /// Convert GRL Rule to TypedReteUlRule
60    fn convert_rule_to_rete(rule: Rule) -> Result<TypedReteUlRule> {
61        // Convert ConditionGroup to ReteUlNode
62        let node = Self::convert_condition_group(&rule.conditions)?;
63
64        // Create RETE rule
65        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    /// Convert ConditionGroup to ReteUlNode
77    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                        // For NOT, we only use left node
97                        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    /// Convert single Condition to ReteUlNode (AlphaNode or UlMultiField)
132    fn convert_condition(condition: &Condition) -> Result<ReteUlNode> {
133        use crate::engine::rule::ConditionExpression;
134
135        // Check if this is a multifield condition
136        match &condition.expression {
137            ConditionExpression::MultiField {
138                field,
139                operation,
140                variable: _,
141            } => {
142                // Convert to UlMultiField node
143                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                // Determine if this is a count operation with comparison
151                let (op, cmp_val) = if operation == "count" && operator_str != "==" {
152                    // Count with comparison: "count > 5"
153                    (Some(operator_str), value_str)
154                } else {
155                    // Other operations
156                    (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                // Standard alpha node for regular conditions
173                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    /// Convert Operator to string
188    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    /// Convert Value to string for AlphaNode
205    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                // Convert array to JSON-like string
214                let items: Vec<String> = arr.iter().map(Self::value_to_string).collect();
215                format!("[{}]", items.join(","))
216            }
217            Value::Object(_) => {
218                // For objects, we'll use a simplified representation
219                "object".to_string()
220            }
221            Value::Expression(expr) => {
222                // For expressions, return the expression string
223                expr.clone()
224            }
225        }
226    }
227
228    /// Create action closure from ActionType list
229    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                // Execute actions
235                for action in &actions {
236                    Self::execute_action(action, facts, results);
237                }
238            },
239        )
240    }
241
242    /// Execute a single action
243    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                // Assignment action (from "field = value" syntax in GRL)
253                // Note: Set() function syntax is NOT supported.
254                // Use: Player.score = Player.score + 10;
255
256                // Check if value is an expression that needs evaluation
257                let evaluated_value = match value {
258                    Value::Expression(expr) => {
259                        // Evaluate expression with current facts
260                        Self::evaluate_expression_for_rete(expr, facts)
261                    }
262                    _ => value.clone(),
263                };
264
265                // Convert evaluated value to FactValue
266                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                // Method calls can be treated as function calls with object as first arg
278                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                // Strip quotes from object name if present
289                let object_name = object.trim_matches('"');
290
291                // Try to get the handle for this fact type from metadata
292                if let Some(handle) = facts.get_fact_handle(object_name) {
293                    // Retract specific fact by handle
294                    results.add(super::ActionResult::Retract(handle));
295                    println!("🗑️ RETRACT: {} (handle: {:?})", object_name, handle);
296                } else {
297                    // Fallback: retract by type (first matching fact)
298                    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                // Treat custom actions as function calls
307                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                // Queue agenda group activation
317                results.add(super::ActionResult::ActivateAgendaGroup(group.clone()));
318                println!("📋 ACTIVATE GROUP: {}", group);
319            }
320            ActionType::ScheduleRule {
321                rule_name,
322                delay_ms,
323            } => {
324                // Queue rule scheduling
325                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                // Mark workflow as completed by setting a fact
333                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                    &timestamp_key,
339                    FactValue::Integer(chrono::Utc::now().timestamp()),
340                );
341
342                println!("✔️ WORKFLOW COMPLETED: {}", workflow_name);
343            }
344            ActionType::SetWorkflowData { key, value } => {
345                // Store workflow data as facts with "workflow.data." prefix
346                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    /// Convert Value to FactValue
356    fn value_to_fact_value(value: &Value) -> FactValue {
357        match value {
358            Value::Number(n) => {
359                // Try integer first, fall back to float
360                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                // For now, treat objects as strings
376                FactValue::String("object".to_string())
377            }
378            Value::Expression(expr) => {
379                // For expressions, store as string - will be evaluated at runtime
380                FactValue::String(format!("[EXPR: {}]", expr))
381            }
382        }
383    }
384
385    /// Extract fact type dependencies from rule
386    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        // Deduplicate
391        deps.sort();
392        deps.dedup();
393
394        deps
395    }
396
397    /// Recursively extract dependencies from ReteUlNode
398    fn extract_deps_from_node(node: &ReteUlNode, deps: &mut Vec<String>) {
399        match node {
400            ReteUlNode::UlAlpha(alpha) => {
401                // Extract fact type from field (e.g., "Person.age" -> "Person")
402                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                // Extract fact type from field (e.g., "Order.items" -> "Order")
409                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                // Add source pattern as a dependency
425                deps.push(source_pattern.clone());
426            }
427            ReteUlNode::UlTerminal(_) => {
428                // Terminal nodes don't have dependencies
429            }
430        }
431    }
432
433    /// Evaluate expression for RETE engine (converts TypedFacts to Facts temporarily)
434    fn evaluate_expression_for_rete(expr: &str, typed_facts: &TypedFacts) -> Value {
435        // Convert TypedFacts to Facts for expression evaluation
436        use crate::engine::facts::Facts;
437
438        let facts = Facts::new();
439
440        // Copy all facts from TypedFacts to Facts
441        // RETE stores facts as "quantity" while GRL uses "Order.quantity"
442        // We need to support both formats
443        for (key, value) in typed_facts.get_all() {
444            let converted_value = Self::fact_value_to_value(value);
445
446            // Store both with and without prefix
447            // E.g., "quantity" -> both "quantity" and "Order.quantity"
448            facts.set(key, converted_value.clone());
449
450            // Also try to add with "Order." prefix if not already present
451            if !key.contains('.') {
452                facts.set(&format!("Order.{}", key), converted_value);
453            }
454        }
455
456        // Evaluate expression
457        match crate::expression::evaluate_expression(expr, &facts) {
458            Ok(result) => result,
459            Err(_e) => {
460                // Silently fallback - this can happen with chained expressions in RETE
461                // due to working memory complexity
462                Value::String(expr.to_string())
463            }
464        }
465    }
466
467    /// Convert FactValue back to Value (reverse of value_to_fact_value)
468    fn fact_value_to_value(fact_value: &FactValue) -> Value {
469        match fact_value {
470            FactValue::String(s) => {
471                // Try to parse as number first
472                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}