rust_logic_graph/rule/
engine.rs1use 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
8pub struct RuleEngine {
10 engine: RustRuleEngine,
11}
12
13impl RuleEngine {
14 pub fn new() -> Self {
16 let kb = KnowledgeBase::new("LogicGraph");
17 Self {
18 engine: RustRuleEngine::new(kb),
19 }
20 }
21
22 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 pub fn evaluate(&mut self, context: &HashMap<String, Value>) -> RuleResult {
37 let mut facts = Facts::new();
39
40 for (key, value) in context {
41 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 match self.engine.execute(&mut facts) {
63 Ok(_) => {
64 debug!("Rules executed successfully");
65 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 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#[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 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 pub fn from_simple(id: impl Into<String>, condition: &str, action: &str) -> Self {
114 let id = id.into();
115
116 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 }
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}