rust_rule_engine/engine/
knowledge_base.rs

1use crate::engine::rule::Rule;
2use crate::errors::{Result, RuleEngineError};
3use crate::parser::grl::GRLParser;
4use crate::types::Value;
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7
8/// Knowledge Base - manages collections of rules and facts
9/// Similar to Grule's KnowledgeBase concept
10#[derive(Debug)]
11pub struct KnowledgeBase {
12    name: String,
13    rules: Arc<RwLock<Vec<Rule>>>,
14    rule_index: Arc<RwLock<HashMap<String, usize>>>,
15    version: Arc<RwLock<u64>>,
16}
17
18impl KnowledgeBase {
19    /// Create a new knowledge base
20    pub fn new(name: &str) -> Self {
21        Self {
22            name: name.to_string(),
23            rules: Arc::new(RwLock::new(Vec::new())),
24            rule_index: Arc::new(RwLock::new(HashMap::new())),
25            version: Arc::new(RwLock::new(0)),
26        }
27    }
28
29    /// Get the knowledge base name
30    pub fn name(&self) -> &str {
31        &self.name
32    }
33
34    /// Get the current version of the knowledge base
35    pub fn version(&self) -> u64 {
36        *self.version.read().unwrap()
37    }
38
39    /// Add a rule to the knowledge base
40    pub fn add_rule(&self, rule: Rule) -> Result<()> {
41        let mut rules = self.rules.write().unwrap();
42        let mut index = self.rule_index.write().unwrap();
43        let mut version = self.version.write().unwrap();
44
45        // Check for duplicate rule names
46        if index.contains_key(&rule.name) {
47            return Err(RuleEngineError::ParseError {
48                message: format!("Rule '{}' already exists", rule.name),
49            });
50        }
51
52        let rule_position = rules.len();
53        index.insert(rule.name.clone(), rule_position);
54        rules.push(rule);
55
56        // Sort rules by priority (salience)
57        rules.sort_by(|a, b| b.salience.cmp(&a.salience));
58
59        // Rebuild index after sorting
60        index.clear();
61        for (pos, rule) in rules.iter().enumerate() {
62            index.insert(rule.name.clone(), pos);
63        }
64
65        *version += 1;
66        Ok(())
67    }
68
69    /// Add multiple rules from GRL text
70    pub fn add_rules_from_grl(&self, grl_text: &str) -> Result<usize> {
71        let rules = GRLParser::parse_rules(grl_text)?;
72        let count = rules.len();
73
74        for rule in rules {
75            self.add_rule(rule)?;
76        }
77
78        Ok(count)
79    }
80
81    /// Remove a rule by name
82    pub fn remove_rule(&self, rule_name: &str) -> Result<bool> {
83        let mut rules = self.rules.write().unwrap();
84        let mut index = self.rule_index.write().unwrap();
85        let mut version = self.version.write().unwrap();
86
87        if let Some(&position) = index.get(rule_name) {
88            rules.remove(position);
89
90            // Rebuild index
91            index.clear();
92            for (pos, rule) in rules.iter().enumerate() {
93                index.insert(rule.name.clone(), pos);
94            }
95
96            *version += 1;
97            Ok(true)
98        } else {
99            Ok(false)
100        }
101    }
102
103    /// Get a rule by name
104    pub fn get_rule(&self, rule_name: &str) -> Option<Rule> {
105        let rules = self.rules.read().unwrap();
106        let index = self.rule_index.read().unwrap();
107
108        if let Some(&position) = index.get(rule_name) {
109            rules.get(position).cloned()
110        } else {
111            None
112        }
113    }
114
115    /// Get all rules
116    pub fn get_rules(&self) -> Vec<Rule> {
117        let rules = self.rules.read().unwrap();
118        rules.clone()
119    }
120
121    /// Get all rule names
122    pub fn get_rule_names(&self) -> Vec<String> {
123        let index = self.rule_index.read().unwrap();
124        index.keys().cloned().collect()
125    }
126
127    /// Get rule count
128    pub fn rule_count(&self) -> usize {
129        let rules = self.rules.read().unwrap();
130        rules.len()
131    }
132
133    /// Enable or disable a rule
134    pub fn set_rule_enabled(&self, rule_name: &str, enabled: bool) -> Result<bool> {
135        let mut rules = self.rules.write().unwrap();
136        let index = self.rule_index.read().unwrap();
137        let mut version = self.version.write().unwrap();
138
139        if let Some(&position) = index.get(rule_name) {
140            if let Some(rule) = rules.get_mut(position) {
141                rule.enabled = enabled;
142                *version += 1;
143                Ok(true)
144            } else {
145                Ok(false)
146            }
147        } else {
148            Ok(false)
149        }
150    }
151
152    /// Clear all rules
153    pub fn clear(&self) {
154        let mut rules = self.rules.write().unwrap();
155        let mut index = self.rule_index.write().unwrap();
156        let mut version = self.version.write().unwrap();
157
158        rules.clear();
159        index.clear();
160        *version += 1;
161    }
162
163    /// Get a snapshot of all rules (for execution)
164    pub fn get_rules_snapshot(&self) -> Vec<Rule> {
165        let rules = self.rules.read().unwrap();
166        rules.clone()
167    }
168
169    /// Get knowledge base statistics
170    pub fn get_statistics(&self) -> KnowledgeBaseStats {
171        let rules = self.rules.read().unwrap();
172
173        let enabled_count = rules.iter().filter(|r| r.enabled).count();
174        let disabled_count = rules.len() - enabled_count;
175
176        let mut priority_distribution = HashMap::new();
177        for rule in rules.iter() {
178            *priority_distribution.entry(rule.salience).or_insert(0) += 1;
179        }
180
181        KnowledgeBaseStats {
182            name: self.name.clone(),
183            version: self.version(),
184            total_rules: rules.len(),
185            enabled_rules: enabled_count,
186            disabled_rules: disabled_count,
187            priority_distribution,
188        }
189    }
190
191    /// Export rules to GRL format
192    pub fn export_to_grl(&self) -> String {
193        let rules = self.rules.read().unwrap();
194        let mut grl_output = String::new();
195
196        grl_output.push_str(&format!("// Knowledge Base: {}\n", self.name));
197        grl_output.push_str(&format!("// Version: {}\n", self.version()));
198        grl_output.push_str(&format!("// Rules: {}\n\n", rules.len()));
199
200        for rule in rules.iter() {
201            grl_output.push_str(&rule.to_grl());
202            grl_output.push_str("\n\n");
203        }
204
205        grl_output
206    }
207}
208
209impl Clone for KnowledgeBase {
210    fn clone(&self) -> Self {
211        let rules = self.rules.read().unwrap();
212        let new_kb = KnowledgeBase::new(&self.name);
213
214        for rule in rules.iter() {
215            let _ = new_kb.add_rule(rule.clone());
216        }
217
218        new_kb
219    }
220}
221
222/// Statistics about a Knowledge Base
223#[derive(Debug, Clone)]
224pub struct KnowledgeBaseStats {
225    /// The name of the knowledge base
226    pub name: String,
227    /// The version number of the knowledge base
228    pub version: u64,
229    /// Total number of rules in the knowledge base
230    pub total_rules: usize,
231    /// Number of enabled rules
232    pub enabled_rules: usize,
233    /// Number of disabled rules
234    pub disabled_rules: usize,
235    /// Distribution of rules by priority/salience
236    pub priority_distribution: HashMap<i32, usize>,
237}
238
239impl std::fmt::Display for KnowledgeBaseStats {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        writeln!(f, "Knowledge Base: {}", self.name)?;
242        writeln!(f, "Version: {}", self.version)?;
243        writeln!(f, "Total Rules: {}", self.total_rules)?;
244        writeln!(f, "Enabled Rules: {}", self.enabled_rules)?;
245        writeln!(f, "Disabled Rules: {}", self.disabled_rules)?;
246        writeln!(f, "Priority Distribution:")?;
247
248        let mut priorities: Vec<_> = self.priority_distribution.iter().collect();
249        priorities.sort_by(|a, b| b.0.cmp(a.0));
250
251        for (priority, count) in priorities {
252            writeln!(f, "  Priority {}: {} rules", priority, count)?;
253        }
254
255        Ok(())
256    }
257}
258
259/// Extension trait to add GRL export functionality to Rule
260trait RuleGRLExport {
261    fn to_grl(&self) -> String;
262}
263
264impl RuleGRLExport for Rule {
265    fn to_grl(&self) -> String {
266        let mut grl = String::new();
267
268        // Rule declaration
269        grl.push_str(&format!("rule {}", self.name));
270
271        if let Some(ref description) = self.description {
272            grl.push_str(&format!(" \"{}\"", description));
273        }
274
275        if self.salience != 0 {
276            grl.push_str(&format!(" salience {}", self.salience));
277        }
278
279        grl.push_str(" {\n");
280
281        // When clause
282        grl.push_str("    when\n");
283        grl.push_str(&format!("        {}\n", self.conditions.to_grl()));
284
285        // Then clause
286        grl.push_str("    then\n");
287        for action in &self.actions {
288            grl.push_str(&format!("        {};\n", action.to_grl()));
289        }
290
291        grl.push('}');
292
293        if !self.enabled {
294            grl = format!("// DISABLED\n{}", grl);
295        }
296
297        grl
298    }
299}
300
301/// Extension trait for ConditionGroup GRL export
302trait ConditionGroupGRLExport {
303    fn to_grl(&self) -> String;
304}
305
306impl ConditionGroupGRLExport for crate::engine::rule::ConditionGroup {
307    fn to_grl(&self) -> String {
308        match self {
309            crate::engine::rule::ConditionGroup::Single(condition) => {
310                format!(
311                    "{} {} {}",
312                    condition.field,
313                    condition.operator.to_grl(),
314                    condition.value.to_grl()
315                )
316            }
317            crate::engine::rule::ConditionGroup::Compound {
318                left,
319                operator,
320                right,
321            } => {
322                let op_str = match operator {
323                    crate::types::LogicalOperator::And => "&&",
324                    crate::types::LogicalOperator::Or => "||",
325                    crate::types::LogicalOperator::Not => "!",
326                };
327                format!("{} {} {}", left.to_grl(), op_str, right.to_grl())
328            }
329            crate::engine::rule::ConditionGroup::Not(condition) => {
330                format!("!{}", condition.to_grl())
331            }
332            crate::engine::rule::ConditionGroup::Exists(condition) => {
333                format!("exists({})", condition.to_grl())
334            }
335            crate::engine::rule::ConditionGroup::Forall(condition) => {
336                format!("forall({})", condition.to_grl())
337            }
338        }
339    }
340}
341
342/// Extension trait for Operator GRL export
343trait OperatorGRLExport {
344    fn to_grl(&self) -> &'static str;
345}
346
347impl OperatorGRLExport for crate::types::Operator {
348    fn to_grl(&self) -> &'static str {
349        match self {
350            crate::types::Operator::Equal => "==",
351            crate::types::Operator::NotEqual => "!=",
352            crate::types::Operator::GreaterThan => ">",
353            crate::types::Operator::GreaterThanOrEqual => ">=",
354            crate::types::Operator::LessThan => "<",
355            crate::types::Operator::LessThanOrEqual => "<=",
356            crate::types::Operator::Contains => "contains",
357            crate::types::Operator::NotContains => "not_contains",
358            crate::types::Operator::StartsWith => "starts_with",
359            crate::types::Operator::EndsWith => "ends_with",
360            crate::types::Operator::Matches => "matches",
361        }
362    }
363}
364
365/// Extension trait for Value GRL export
366trait ValueGRLExport {
367    fn to_grl(&self) -> String;
368}
369
370impl ValueGRLExport for Value {
371    fn to_grl(&self) -> String {
372        match self {
373            Value::String(s) => format!("\"{}\"", s),
374            Value::Number(n) => n.to_string(),
375            Value::Integer(i) => i.to_string(),
376            Value::Boolean(b) => b.to_string(),
377            Value::Null => "null".to_string(),
378            Value::Array(_) => "[array]".to_string(),
379            Value::Object(_) => "{object}".to_string(),
380        }
381    }
382}
383
384/// Extension trait for ActionType GRL export
385trait ActionTypeGRLExport {
386    fn to_grl(&self) -> String;
387}
388
389impl ActionTypeGRLExport for crate::types::ActionType {
390    fn to_grl(&self) -> String {
391        match self {
392            crate::types::ActionType::Set { field, value } => {
393                format!("{} = {}", field, value.to_grl())
394            }
395            crate::types::ActionType::Log { message } => {
396                format!("Log(\"{}\")", message)
397            }
398            crate::types::ActionType::MethodCall {
399                object,
400                method,
401                args,
402            } => {
403                let args_str = args
404                    .iter()
405                    .map(|arg| arg.to_grl())
406                    .collect::<Vec<_>>()
407                    .join(", ");
408                format!("{}.{}({})", object, method, args_str)
409            }
410            crate::types::ActionType::Update { object } => {
411                format!("update({})", object)
412            }
413            crate::types::ActionType::Call { function, args } => {
414                let args_str = args
415                    .iter()
416                    .map(|arg| arg.to_grl())
417                    .collect::<Vec<_>>()
418                    .join(", ");
419                format!("{}({})", function, args_str)
420            }
421            crate::types::ActionType::Custom { action_type, .. } => {
422                format!("Custom(\"{}\")", action_type)
423            }
424        }
425    }
426}