rust_logic_graph/rule/
engine.rs

1use rust_rule_engine::{RustRuleEngine, Facts, KnowledgeBase, GRLParser, Value as RRValue, };
2use serde_json::Value;
3use std::collections::HashMap;
4use tracing::{debug, warn};
5
6use super::{RuleError, RuleResult};
7
8/// Advanced rule engine powered by rust-rule-engine
9pub struct RuleEngine {
10    engine: RustRuleEngine,
11}
12
13impl RuleEngine {
14    /// Create a new rule engine with default knowledge base
15    pub fn new() -> Self {
16        let kb = KnowledgeBase::new("LogicGraph");
17        Self {
18            engine: RustRuleEngine::new(kb),
19        }
20    }
21
22    /// Add a rule using GRL (Grule Rule Language) syntax
23    pub fn add_grl_rule(&mut self, grl_content: &str) -> Result<(), RuleError> {
24        let rules = GRLParser::parse_rules(grl_content)
25            .map_err(|e| RuleError::Eval(format!("Failed to parse GRL: {}", e)))?;
26
27        for rule in rules {
28            self.engine.knowledge_base().add_rule(rule)
29                .map_err(|e| RuleError::Eval(format!("Failed to add rule: {}", e)))?;
30        }
31
32        Ok(())
33    }
34
35    /// Evaluate all rules against the given context
36    pub fn evaluate(&mut self, context: &HashMap<String, Value>) -> RuleResult {
37        // Convert context to Facts
38        let mut facts = Facts::new();
39
40        for (key, value) in context {
41            // Convert serde_json::Value to rust_rule_engine::Value
42            let rr_value = match value {
43                Value::Bool(b) => RRValue::Boolean(*b),
44                Value::Number(n) => {
45                    if let Some(f) = n.as_f64() {
46                        RRValue::Number(f)
47                    } else {
48                        continue;
49                    }
50                }
51                Value::String(s) => RRValue::String(s.clone()),
52                _ => {
53                    debug!("Skipping unsupported value type for key: {}", key);
54                    continue;
55                }
56            };
57
58            facts.set(&key, rr_value);
59        }
60
61        // Execute rules
62        match self.engine.execute(&mut facts) {
63            Ok(_) => {
64                debug!("Rules executed successfully");
65                // Return success indicator
66                Ok(Value::Bool(true))
67            }
68            Err(e) => {
69                warn!("Rule execution failed: {}", e);
70                Err(RuleError::Eval(format!("Rule execution failed: {}", e)))
71            }
72        }
73    }
74
75    /// Create a rule engine from GRL script content
76    pub fn from_grl(grl_script: &str) -> Result<Self, RuleError> {
77        let mut engine = Self::new();
78        engine.add_grl_rule(grl_script)?;
79        Ok(engine)
80    }
81}
82
83impl Default for RuleEngine {
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89/// Advanced rule with GRL support
90#[derive(Debug, Clone)]
91pub struct GrlRule {
92    pub id: String,
93    pub grl_content: String,
94}
95
96impl GrlRule {
97    pub fn new(id: impl Into<String>, grl_content: impl Into<String>) -> Self {
98        Self {
99            id: id.into(),
100            grl_content: grl_content.into(),
101        }
102    }
103
104    /// Evaluate the GRL rule
105    pub fn evaluate(&self, context: &HashMap<String, Value>) -> RuleResult {
106        let mut engine = RuleEngine::new();
107        engine.add_grl_rule(&self.grl_content)?;
108        engine.evaluate(context)
109    }
110
111    /// Create GRL rule from simple condition
112    /// Example: GrlRule::from_simple("age_check", "age >= 18", "eligible = true")
113    pub fn from_simple(id: impl Into<String>, condition: &str, action: &str) -> Self {
114        let id = id.into();
115
116        // Convert to GRL format
117        let grl_content = format!(
118            r#"
119rule "{}" {{
120    when
121        {}
122    then
123        {};
124}}
125"#,
126            id, condition, action
127        );
128
129        Self {
130            id,
131            grl_content,
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_rule_engine_creation() {
142        let _engine = RuleEngine::new();
143        // Just test creation works
144    }
145
146    #[test]
147    fn test_grl_rule_creation() {
148        let rule = GrlRule::new("test", "rule test { when true then }");
149        assert_eq!(rule.id, "test");
150    }
151
152    #[test]
153    fn test_rule_from_simple() {
154        let rule = GrlRule::from_simple("age_check", "age >= 18", "eligible = true");
155        assert!(rule.grl_content.contains("age >= 18"));
156        assert!(rule.grl_content.contains("eligible = true"));
157    }
158}