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
6use 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
16/// GRL to RETE Loader
17/// Converts GRL rules into RETE-UL structures
18pub struct GrlReteLoader;
19
20impl GrlReteLoader {
21    /// Load rules from a GRL file into RETE engine
22    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    /// Load rules from GRL string into RETE engine
36    pub fn load_from_string(
37        grl_text: &str,
38        engine: &mut IncrementalEngine,
39    ) -> Result<usize> {
40        // Parse GRL rules
41        let rules = GRLParser::parse_rules(grl_text)?;
42
43        let mut loaded_count = 0;
44
45        for rule in rules {
46            // Convert GRL rule to RETE rule
47            let rete_rule = Self::convert_rule_to_rete(rule)?;
48
49            // Extract dependencies (fact types used in conditions)
50            let dependencies = Self::extract_dependencies(&rete_rule);
51
52            // Add to engine
53            engine.add_rule(rete_rule, dependencies);
54            loaded_count += 1;
55        }
56
57        Ok(loaded_count)
58    }
59
60    /// Convert GRL Rule to TypedReteUlRule
61    fn convert_rule_to_rete(rule: Rule) -> Result<TypedReteUlRule> {
62        // Convert ConditionGroup to ReteUlNode
63        let node = Self::convert_condition_group(&rule.conditions)?;
64
65        // Create RETE rule
66        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    /// Convert ConditionGroup to ReteUlNode
78    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                        // For NOT, we only use left node
96                        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            ConditionGroup::Accumulate {
113                result_var,
114                source_pattern,
115                extract_field,
116                source_conditions,
117                function,
118                function_arg,
119            } => {
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
132    /// Convert single Condition to ReteUlNode (AlphaNode or UlMultiField)
133    fn convert_condition(condition: &Condition) -> Result<ReteUlNode> {
134        use crate::engine::rule::ConditionExpression;
135
136        // Check if this is a multifield condition
137        match &condition.expression {
138            ConditionExpression::MultiField { field, operation, variable } => {
139                // Convert to UlMultiField node
140                let operator_str = Self::operator_to_string(&condition.operator);
141                let value_str = if !matches!(condition.value, Value::Boolean(_)) {
142                    Some(Self::value_to_string(&condition.value))
143                } else {
144                    None
145                };
146
147                // Determine if this is a count operation with comparison
148                let (op, cmp_val) = if operation == "count" && operator_str != "==" {
149                    // Count with comparison: "count > 5"
150                    (Some(operator_str), value_str)
151                } else {
152                    // Other operations
153                    (None, value_str)
154                };
155
156                Ok(ReteUlNode::UlMultiField {
157                    field: field.clone(),
158                    operation: operation.clone(),
159                    value: if operation == "contains" { cmp_val.clone() } else { None },
160                    operator: op,
161                    compare_value: if operation == "count" { cmp_val } else { None },
162                })
163            }
164            _ => {
165                // Standard alpha node for regular conditions
166                let operator_str = Self::operator_to_string(&condition.operator);
167                let value_str = Self::value_to_string(&condition.value);
168
169                let alpha = AlphaNode {
170                    field: condition.field.clone(),
171                    operator: operator_str,
172                    value: value_str,
173                };
174
175                Ok(ReteUlNode::UlAlpha(alpha))
176            }
177        }
178    }
179
180    /// Convert Operator to string
181    fn operator_to_string(op: &Operator) -> String {
182        match op {
183            Operator::Equal => "==".to_string(),
184            Operator::NotEqual => "!=".to_string(),
185            Operator::GreaterThan => ">".to_string(),
186            Operator::GreaterThanOrEqual => ">=".to_string(),
187            Operator::LessThan => "<".to_string(),
188            Operator::LessThanOrEqual => "<=".to_string(),
189            Operator::Contains => "contains".to_string(),
190            Operator::NotContains => "!contains".to_string(),
191            Operator::StartsWith => "startsWith".to_string(),
192            Operator::EndsWith => "endsWith".to_string(),
193            Operator::Matches => "matches".to_string(),
194        }
195    }
196
197    /// Convert Value to string for AlphaNode
198    fn value_to_string(value: &Value) -> String {
199        match value {
200            Value::Number(n) => n.to_string(),
201            Value::Integer(i) => i.to_string(),
202            Value::String(s) => s.clone(),
203            Value::Boolean(b) => b.to_string(),
204            Value::Null => "null".to_string(),
205            Value::Array(arr) => {
206                // Convert array to JSON-like string
207                let items: Vec<String> = arr.iter()
208                    .map(|v| Self::value_to_string(v))
209                    .collect();
210                format!("[{}]", items.join(","))
211            }
212            Value::Object(_) => {
213                // For objects, we'll use a simplified representation
214                "object".to_string()
215            }
216            Value::Expression(expr) => {
217                // For expressions, return the expression string
218                expr.clone()
219            }
220        }
221    }
222
223    /// Create action closure from ActionType list
224    fn create_action_closure(
225        actions: Vec<crate::types::ActionType>,
226    ) -> std::sync::Arc<dyn Fn(&mut TypedFacts, &mut super::ActionResults) + Send + Sync> {
227        std::sync::Arc::new(move |facts: &mut TypedFacts, results: &mut super::ActionResults| {
228            // Execute actions
229            for action in &actions {
230                Self::execute_action(action, facts, results);
231            }
232        })
233    }
234
235    /// Execute a single action
236    fn execute_action(
237        action: &crate::types::ActionType, 
238        facts: &mut TypedFacts,
239        results: &mut super::ActionResults,
240    ) {
241        use crate::types::ActionType;
242
243        match action {
244            ActionType::Set { field, value } => {
245                // Check if value is an expression that needs evaluation
246                let evaluated_value = match value {
247                    Value::Expression(expr) => {
248                        // Evaluate expression with current facts
249                        Self::evaluate_expression_for_rete(expr, facts)
250                    }
251                    _ => value.clone(),
252                };
253
254                // Convert evaluated value to FactValue
255                let fact_value = Self::value_to_fact_value(&evaluated_value);
256                facts.set(field, fact_value);
257            }
258            ActionType::Log { message } => {
259                println!("📝 LOG: {}", message);
260            }
261            ActionType::Call { function, args } => {
262                // Queue function call to be executed by engine
263                results.add(super::ActionResult::CallFunction {
264                    function_name: function.clone(),
265                    args: args.iter().map(|v| Self::value_to_string(v)).collect(),
266                });
267                println!("🔧 CALL: {}", function);
268            }
269            ActionType::MethodCall { object, method, args } => {
270                // Method calls can be treated as function calls with object as first arg
271                let mut all_args = vec![object.clone()];
272                all_args.extend(args.iter().map(|v| Self::value_to_string(v)));
273                
274                results.add(super::ActionResult::CallFunction {
275                    function_name: format!("{}.{}", object, method),
276                    args: all_args,
277                });
278                println!("� METHOD: {}.{}", object, method);
279            }
280            ActionType::Update { object } => {
281                // Mark that this fact type should trigger re-evaluation
282                // We'll use RetractByType as a signal (won't actually retract, just re-evaluate)
283                println!("� UPDATE: {}", object);
284                // Note: Update is handled automatically by the engine after fact modifications
285            }
286            ActionType::Retract { object } => {
287                // Strip quotes from object name if present
288                let object_name = object.trim_matches('"');
289                
290                // Try to get the handle for this fact type from metadata
291                if let Some(handle) = facts.get_fact_handle(object_name) {
292                    // Retract specific fact by handle
293                    results.add(super::ActionResult::Retract(handle));
294                    println!("🗑️ RETRACT: {} (handle: {:?})", object_name, handle);
295                } else {
296                    // Fallback: retract by type (first matching fact)
297                    results.add(super::ActionResult::RetractByType(object_name.to_string()));
298                    println!("🗑️ RETRACT: {} (by type, no handle found)", object_name);
299                }
300            }
301            ActionType::Custom { action_type, params } => {
302                println!("⚙️ CUSTOM: {}", action_type);
303                
304                // Handle built-in custom functions
305                match action_type.as_str() {
306                    "set" => {
307                        // set(field, value) - params should have "0" and "1" keys for positional args
308                        // or "field" and "value" for named args
309                        if let Some(field_val) = params.get("0").or_else(|| params.get("field")) {
310                            if let Some(value_val) = params.get("1").or_else(|| params.get("value")) {
311                                // Extract field name from first parameter
312                                let field_str = match field_val {
313                                    Value::String(s) => s.clone(),
314                                    Value::Expression(expr) => format!("{:?}", expr),
315                                    _ => return,
316                                };
317                                
318                                // Clean up field string
319                                let field_clean = field_str
320                                    .trim_matches(|c: char| !c.is_alphanumeric() && c != '.')
321                                    .to_string();
322                                
323                                // Evaluate the value expression
324                                let evaluated_value = match value_val {
325                                    Value::Expression(expr) => {
326                                        Self::evaluate_expression_for_rete(expr, facts)
327                                    }
328                                    _ => value_val.clone(),
329                                };
330                                
331                                // Set the value
332                                let fact_value = Self::value_to_fact_value(&evaluated_value);
333                                facts.set(&field_clean, fact_value);
334                            }
335                        }
336                    }
337                    _ => {
338                        // Unknown custom action, just log it
339                    }
340                }
341            }
342            ActionType::ActivateAgendaGroup { group } => {
343                // Queue agenda group activation
344                results.add(super::ActionResult::ActivateAgendaGroup(group.clone()));
345                println!("📋 ACTIVATE GROUP: {}", group);
346            }
347            ActionType::ScheduleRule { rule_name, delay_ms } => {
348                // Queue rule scheduling
349                results.add(super::ActionResult::ScheduleRule {
350                    rule_name: rule_name.clone(),
351                    delay_ms: *delay_ms,
352                });
353                println!("⏰ SCHEDULE: {} (delay: {}ms)", rule_name, delay_ms);
354            }
355            ActionType::CompleteWorkflow { workflow_name } => {
356                println!("✔️ COMPLETE WORKFLOW: {}", workflow_name);
357            }
358            ActionType::SetWorkflowData { key, value: _ } => {
359                println!("📊 SET WORKFLOW DATA: {}", key);
360            }
361        }
362    }
363
364    /// Convert Value to FactValue
365    fn value_to_fact_value(value: &Value) -> FactValue {
366        match value {
367            Value::Number(n) => {
368                // Try integer first, fall back to float
369                if n.fract() == 0.0 {
370                    FactValue::Integer(*n as i64)
371                } else {
372                    FactValue::Float(*n)
373                }
374            }
375            Value::Integer(i) => FactValue::Integer(*i),
376            Value::String(s) => FactValue::String(s.clone()),
377            Value::Boolean(b) => FactValue::Boolean(*b),
378            Value::Null => FactValue::Null,
379            Value::Array(arr) => {
380                let fact_arr: Vec<FactValue> = arr.iter()
381                    .map(Self::value_to_fact_value)
382                    .collect();
383                FactValue::Array(fact_arr)
384            }
385            Value::Object(_) => {
386                // For now, treat objects as strings
387                FactValue::String("object".to_string())
388            }
389            Value::Expression(expr) => {
390                // For expressions, store as string - will be evaluated at runtime
391                FactValue::String(format!("[EXPR: {}]", expr))
392            }
393        }
394    }
395
396    /// Extract fact type dependencies from rule
397    fn extract_dependencies(rule: &TypedReteUlRule) -> Vec<String> {
398        let mut deps = Vec::new();
399        Self::extract_deps_from_node(&rule.node, &mut deps);
400
401        // Deduplicate
402        deps.sort();
403        deps.dedup();
404
405        deps
406    }
407
408    /// Recursively extract dependencies from ReteUlNode
409    fn extract_deps_from_node(node: &ReteUlNode, deps: &mut Vec<String>) {
410        match node {
411            ReteUlNode::UlAlpha(alpha) => {
412                // Extract fact type from field (e.g., "Person.age" -> "Person")
413                if let Some(dot_pos) = alpha.field.find('.') {
414                    let fact_type = alpha.field[..dot_pos].to_string();
415                    deps.push(fact_type);
416                }
417            }
418            ReteUlNode::UlMultiField { field, .. } => {
419                // Extract fact type from field (e.g., "Order.items" -> "Order")
420                if let Some(dot_pos) = field.find('.') {
421                    let fact_type = field[..dot_pos].to_string();
422                    deps.push(fact_type);
423                }
424            }
425            ReteUlNode::UlAnd(left, right) | ReteUlNode::UlOr(left, right) => {
426                Self::extract_deps_from_node(left, deps);
427                Self::extract_deps_from_node(right, deps);
428            }
429            ReteUlNode::UlNot(inner)
430            | ReteUlNode::UlExists(inner)
431            | ReteUlNode::UlForall(inner) => {
432                Self::extract_deps_from_node(inner, deps);
433            }
434            ReteUlNode::UlAccumulate { source_pattern, .. } => {
435                // Add source pattern as a dependency
436                deps.push(source_pattern.clone());
437            }
438            ReteUlNode::UlTerminal(_) => {
439                // Terminal nodes don't have dependencies
440            }
441        }
442    }
443
444    /// Evaluate expression for RETE engine (converts TypedFacts to Facts temporarily)
445    fn evaluate_expression_for_rete(expr: &str, typed_facts: &TypedFacts) -> Value {
446        // Convert TypedFacts to Facts for expression evaluation
447        use crate::engine::facts::Facts;
448
449        let mut facts = Facts::new();
450
451        // Copy all facts from TypedFacts to Facts
452        // RETE stores facts as "quantity" while GRL uses "Order.quantity"
453        // We need to support both formats
454        for (key, value) in typed_facts.get_all() {
455            let converted_value = Self::fact_value_to_value(value);
456
457            // Store both with and without prefix
458            // E.g., "quantity" -> both "quantity" and "Order.quantity"
459            facts.set(key, converted_value.clone());
460
461            // Also try to add with "Order." prefix if not already present
462            if !key.contains('.') {
463                facts.set(&format!("Order.{}", key), converted_value);
464            }
465        }
466
467        // Evaluate expression
468        match crate::expression::evaluate_expression(expr, &facts) {
469            Ok(result) => result,
470            Err(e) => {
471                // Silently fallback - this can happen with chained expressions in RETE
472                // due to working memory complexity
473                Value::String(expr.to_string())
474            }
475        }
476    }
477
478    /// Convert FactValue back to Value (reverse of value_to_fact_value)
479    fn fact_value_to_value(fact_value: &FactValue) -> Value {
480        match fact_value {
481            FactValue::String(s) => {
482                // Try to parse as number first
483                if let Ok(i) = s.parse::<i64>() {
484                    Value::Integer(i)
485                } else if let Ok(f) = s.parse::<f64>() {
486                    Value::Number(f)
487                } else if s == "true" {
488                    Value::Boolean(true)
489                } else if s == "false" {
490                    Value::Boolean(false)
491                } else {
492                    Value::String(s.clone())
493                }
494            }
495            FactValue::Integer(i) => Value::Integer(*i),
496            FactValue::Float(f) => Value::Number(*f),
497            FactValue::Boolean(b) => Value::Boolean(*b),
498            FactValue::Array(arr) => {
499                Value::Array(arr.iter().map(Self::fact_value_to_value).collect())
500            }
501            FactValue::Null => Value::Null,
502        }
503    }
504}
505
506#[cfg(test)]
507mod tests {
508    use super::*;
509
510    #[test]
511    fn test_convert_simple_rule() {
512        let grl = r#"
513        rule "TestRule" salience 10 no-loop {
514            when
515                Person.age > 18
516            then
517                Person.is_adult = true;
518        }
519        "#;
520
521        let rules = GRLParser::parse_rules(grl).unwrap();
522        assert_eq!(rules.len(), 1);
523
524        let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
525        assert_eq!(rete_rule.name, "TestRule");
526        assert_eq!(rete_rule.priority, 10);
527        assert!(rete_rule.no_loop);
528    }
529
530    #[test]
531    fn test_extract_dependencies() {
532        let grl = r#"
533        rule "MultiTypeRule" {
534            when
535                Person.age > 18 && Order.amount > 1000
536            then
537                Person.premium = true;
538        }
539        "#;
540
541        let rules = GRLParser::parse_rules(grl).unwrap();
542        let rete_rule = GrlReteLoader::convert_rule_to_rete(rules[0].clone()).unwrap();
543        let deps = GrlReteLoader::extract_dependencies(&rete_rule);
544
545        assert_eq!(deps.len(), 2);
546        assert!(deps.contains(&"Person".to_string()));
547        assert!(deps.contains(&"Order".to_string()));
548    }
549
550    #[test]
551    fn test_load_from_string() {
552        let grl = r#"
553        rule "Rule1" {
554            when
555                Person.age > 18
556            then
557                Person.is_adult = true;
558        }
559
560        rule "Rule2" {
561            when
562                Order.amount > 1000
563            then
564                Order.high_value = true;
565        }
566        "#;
567
568        let mut engine = IncrementalEngine::new();
569        let count = GrlReteLoader::load_from_string(grl, &mut engine).unwrap();
570
571        assert_eq!(count, 2);
572    }
573}