1#![allow(deprecated)]
2
3use crate::engine::rule::Rule;
4use crate::errors::{Result, RuleEngineError};
5use crate::parser::grl::GRLParser;
6use crate::types::Value;
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9
10#[derive(Debug)]
13pub struct KnowledgeBase {
14 name: String,
15 rules: Arc<RwLock<Vec<Rule>>>,
16 rule_index: Arc<RwLock<HashMap<String, usize>>>,
17 version: Arc<RwLock<u64>>,
18}
19
20impl KnowledgeBase {
21 pub fn new(name: &str) -> Self {
23 Self {
24 name: name.to_string(),
25 rules: Arc::new(RwLock::new(Vec::new())),
26 rule_index: Arc::new(RwLock::new(HashMap::new())),
27 version: Arc::new(RwLock::new(0)),
28 }
29 }
30
31 pub fn name(&self) -> &str {
33 &self.name
34 }
35
36 pub fn version(&self) -> u64 {
38 *self.version.read().unwrap()
39 }
40
41 pub fn add_rule(&self, rule: Rule) -> Result<()> {
43 let mut rules = self.rules.write().unwrap();
44 let mut index = self.rule_index.write().unwrap();
45 let mut version = self.version.write().unwrap();
46
47 if index.contains_key(&rule.name) {
49 return Err(RuleEngineError::ParseError {
50 message: format!("Rule '{}' already exists", rule.name),
51 });
52 }
53
54 let rule_position = rules.len();
55 index.insert(rule.name.clone(), rule_position);
56 rules.push(rule);
57
58 rules.sort_by_key(|b| std::cmp::Reverse(b.salience));
61
62 index.clear();
64 for (pos, rule) in rules.iter().enumerate() {
65 index.insert(rule.name.clone(), pos);
66 }
67
68 *version += 1;
69 Ok(())
70 }
71
72 pub fn add_rules_from_grl(&self, grl_text: &str) -> Result<usize> {
74 let rules = GRLParser::parse_rules(grl_text)?;
75 let count = rules.len();
76
77 for rule in rules {
78 self.add_rule(rule)?;
79 }
80
81 Ok(count)
82 }
83
84 pub fn remove_rule(&self, rule_name: &str) -> Result<bool> {
86 let mut rules = self.rules.write().unwrap();
87 let mut index = self.rule_index.write().unwrap();
88 let mut version = self.version.write().unwrap();
89
90 if let Some(&position) = index.get(rule_name) {
91 rules.remove(position);
92
93 index.clear();
95 for (pos, rule) in rules.iter().enumerate() {
96 index.insert(rule.name.clone(), pos);
97 }
98
99 *version += 1;
100 Ok(true)
101 } else {
102 Ok(false)
103 }
104 }
105
106 pub fn get_rule(&self, rule_name: &str) -> Option<Rule> {
108 let rules = self.rules.read().unwrap();
109 let index = self.rule_index.read().unwrap();
110
111 if let Some(&position) = index.get(rule_name) {
112 rules.get(position).cloned()
113 } else {
114 None
115 }
116 }
117
118 pub fn get_rules(&self) -> Vec<Rule> {
120 let rules = self.rules.read().unwrap();
121 rules.clone()
122 }
123
124 pub fn get_rules_by_salience(&self) -> Vec<usize> {
127 let rules = self.rules.read().unwrap();
128 let mut indices: Vec<usize> = (0..rules.len()).collect();
129 indices.sort_by(|&a, &b| rules[b].salience.cmp(&rules[a].salience));
130 indices
131 }
132
133 pub fn get_rule_by_index(&self, index: usize) -> Option<Rule> {
135 let rules = self.rules.read().unwrap();
136 rules.get(index).cloned()
137 }
138
139 pub fn get_rule_names(&self) -> Vec<String> {
141 let index = self.rule_index.read().unwrap();
142 index.keys().cloned().collect()
143 }
144
145 pub fn rule_count(&self) -> usize {
147 let rules = self.rules.read().unwrap();
148 rules.len()
149 }
150
151 pub fn set_rule_enabled(&self, rule_name: &str, enabled: bool) -> Result<bool> {
153 let mut rules = self.rules.write().unwrap();
154 let index = self.rule_index.read().unwrap();
155 let mut version = self.version.write().unwrap();
156
157 if let Some(&position) = index.get(rule_name) {
158 if let Some(rule) = rules.get_mut(position) {
159 rule.enabled = enabled;
160 *version += 1;
161 Ok(true)
162 } else {
163 Ok(false)
164 }
165 } else {
166 Ok(false)
167 }
168 }
169
170 pub fn clear(&self) {
172 let mut rules = self.rules.write().unwrap();
173 let mut index = self.rule_index.write().unwrap();
174 let mut version = self.version.write().unwrap();
175
176 rules.clear();
177 index.clear();
178 *version += 1;
179 }
180
181 pub fn get_rules_snapshot(&self) -> Vec<Rule> {
183 let rules = self.rules.read().unwrap();
184 rules.clone()
185 }
186
187 pub fn get_statistics(&self) -> KnowledgeBaseStats {
189 let rules = self.rules.read().unwrap();
190
191 let enabled_count = rules.iter().filter(|r| r.enabled).count();
192 let disabled_count = rules.len() - enabled_count;
193
194 let mut priority_distribution = HashMap::new();
195 for rule in rules.iter() {
196 *priority_distribution.entry(rule.salience).or_insert(0) += 1;
197 }
198
199 KnowledgeBaseStats {
200 name: self.name.clone(),
201 version: self.version(),
202 total_rules: rules.len(),
203 enabled_rules: enabled_count,
204 disabled_rules: disabled_count,
205 priority_distribution,
206 }
207 }
208
209 pub fn export_to_grl(&self) -> String {
211 let rules = self.rules.read().unwrap();
212 let mut grl_output = String::new();
213
214 grl_output.push_str(&format!("// Knowledge Base: {}\n", self.name));
215 grl_output.push_str(&format!("// Version: {}\n", self.version()));
216 grl_output.push_str(&format!("// Rules: {}\n\n", rules.len()));
217
218 for rule in rules.iter() {
219 grl_output.push_str(&rule.to_grl());
220 grl_output.push_str("\n\n");
221 }
222
223 grl_output
224 }
225}
226
227impl Clone for KnowledgeBase {
228 fn clone(&self) -> Self {
229 let rules = self.rules.read().unwrap();
230 let new_kb = KnowledgeBase::new(&self.name);
231
232 for rule in rules.iter() {
233 let _ = new_kb.add_rule(rule.clone());
234 }
235
236 new_kb
237 }
238}
239
240#[derive(Debug, Clone)]
242pub struct KnowledgeBaseStats {
243 pub name: String,
245 pub version: u64,
247 pub total_rules: usize,
249 pub enabled_rules: usize,
251 pub disabled_rules: usize,
253 pub priority_distribution: HashMap<i32, usize>,
255}
256
257impl std::fmt::Display for KnowledgeBaseStats {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 writeln!(f, "Knowledge Base: {}", self.name)?;
260 writeln!(f, "Version: {}", self.version)?;
261 writeln!(f, "Total Rules: {}", self.total_rules)?;
262 writeln!(f, "Enabled Rules: {}", self.enabled_rules)?;
263 writeln!(f, "Disabled Rules: {}", self.disabled_rules)?;
264 writeln!(f, "Priority Distribution:")?;
265
266 let mut priorities: Vec<_> = self.priority_distribution.iter().collect();
267 priorities.sort_by(|a, b| b.0.cmp(a.0));
268
269 for (priority, count) in priorities {
270 writeln!(f, " Priority {}: {} rules", priority, count)?;
271 }
272
273 Ok(())
274 }
275}
276
277trait RuleGRLExport {
279 fn to_grl(&self) -> String;
280}
281
282impl RuleGRLExport for Rule {
283 fn to_grl(&self) -> String {
284 let mut grl = String::new();
285
286 grl.push_str(&format!("rule {}", self.name));
288
289 if let Some(ref description) = self.description {
290 grl.push_str(&format!(" \"{}\"", description));
291 }
292
293 if self.salience != 0 {
294 grl.push_str(&format!(" salience {}", self.salience));
295 }
296
297 grl.push_str(" {\n");
298
299 grl.push_str(" when\n");
301 grl.push_str(&format!(" {}\n", self.conditions.to_grl()));
302
303 grl.push_str(" then\n");
305 for action in &self.actions {
306 grl.push_str(&format!(" {};\n", action.to_grl()));
307 }
308
309 grl.push('}');
310
311 if !self.enabled {
312 grl = format!("// DISABLED\n{}", grl);
313 }
314
315 grl
316 }
317}
318
319trait ConditionGroupGRLExport {
321 fn to_grl(&self) -> String;
322}
323
324impl ConditionGroupGRLExport for crate::engine::rule::ConditionGroup {
325 fn to_grl(&self) -> String {
326 match self {
327 crate::engine::rule::ConditionGroup::Single(condition) => {
328 format!(
329 "{} {} {}",
330 condition.field,
331 condition.operator.to_grl(),
332 condition.value.to_grl()
333 )
334 }
335 crate::engine::rule::ConditionGroup::Compound {
336 left,
337 operator,
338 right,
339 } => {
340 let op_str = match operator {
341 crate::types::LogicalOperator::And => "&&",
342 crate::types::LogicalOperator::Or => "||",
343 crate::types::LogicalOperator::Not => "!",
344 };
345 format!("{} {} {}", left.to_grl(), op_str, right.to_grl())
346 }
347 crate::engine::rule::ConditionGroup::Not(condition) => {
348 format!("!{}", condition.to_grl())
349 }
350 crate::engine::rule::ConditionGroup::Exists(condition) => {
351 format!("exists({})", condition.to_grl())
352 }
353 crate::engine::rule::ConditionGroup::Forall(condition) => {
354 format!("forall({})", condition.to_grl())
355 }
356 crate::engine::rule::ConditionGroup::Accumulate {
357 source_pattern,
358 extract_field,
359 source_conditions,
360 function,
361 function_arg,
362 ..
363 } => {
364 let conditions_str = if source_conditions.is_empty() {
365 String::new()
366 } else {
367 format!(", {}", source_conditions.join(", "))
368 };
369 format!(
370 "accumulate({}(${}: {}{}), {}({}))",
371 source_pattern,
372 function_arg.trim_start_matches('$'),
373 extract_field,
374 conditions_str,
375 function,
376 function_arg
377 )
378 }
379
380 #[cfg(feature = "streaming")]
381 crate::engine::rule::ConditionGroup::StreamPattern {
382 var_name,
383 event_type,
384 stream_name,
385 window,
386 } => {
387 let event_type_str = event_type
389 .as_ref()
390 .map(|t| format!("{} ", t))
391 .unwrap_or_default();
392 let window_str = window
393 .as_ref()
394 .map(|w| {
395 let dur_secs = w.duration.as_secs();
396 let (dur_val, dur_unit) = if dur_secs >= 3600 {
397 (dur_secs / 3600, "hour")
398 } else if dur_secs >= 60 {
399 (dur_secs / 60, "min")
400 } else {
401 (dur_secs, "sec")
402 };
403 let window_type_str = match &w.window_type {
404 crate::engine::rule::StreamWindowType::Sliding => "sliding",
405 crate::engine::rule::StreamWindowType::Tumbling => "tumbling",
406 crate::engine::rule::StreamWindowType::Session { .. } => "session",
407 };
408 format!(
409 " over window({} {}, {})",
410 dur_val, dur_unit, window_type_str
411 )
412 })
413 .unwrap_or_default();
414 format!(
415 "{}: {}from stream(\"{}\"){}",
416 var_name, event_type_str, stream_name, window_str
417 )
418 }
419 }
420 }
421}
422
423trait OperatorGRLExport {
425 fn to_grl(&self) -> &'static str;
426}
427
428impl OperatorGRLExport for crate::types::Operator {
429 fn to_grl(&self) -> &'static str {
430 match self {
431 crate::types::Operator::Equal => "==",
432 crate::types::Operator::NotEqual => "!=",
433 crate::types::Operator::GreaterThan => ">",
434 crate::types::Operator::GreaterThanOrEqual => ">=",
435 crate::types::Operator::LessThan => "<",
436 crate::types::Operator::LessThanOrEqual => "<=",
437 crate::types::Operator::Contains => "contains",
438 crate::types::Operator::NotContains => "not_contains",
439 crate::types::Operator::StartsWith => "startsWith",
440 crate::types::Operator::EndsWith => "endsWith",
441 crate::types::Operator::Matches => "matches",
442 crate::types::Operator::In => "in",
443 }
444 }
445}
446
447trait ValueGRLExport {
449 fn to_grl(&self) -> String;
450}
451
452impl ValueGRLExport for Value {
453 fn to_grl(&self) -> String {
454 match self {
455 Value::String(s) => format!("\"{}\"", s),
456 Value::Number(n) => n.to_string(),
457 Value::Integer(i) => i.to_string(),
458 Value::Boolean(b) => b.to_string(),
459 Value::Null => "null".to_string(),
460 Value::Array(_) => "[array]".to_string(),
461 Value::Object(_) => "{object}".to_string(),
462 Value::Expression(expr) => expr.clone(), }
464 }
465}
466
467trait ActionTypeGRLExport {
469 fn to_grl(&self) -> String;
470}
471
472impl ActionTypeGRLExport for crate::types::ActionType {
473 fn to_grl(&self) -> String {
474 match self {
475 crate::types::ActionType::Set { field, value } => {
476 format!("{} = {}", field, value.to_grl())
477 }
478 crate::types::ActionType::Log { message } => {
479 format!("Log(\"{}\")", message)
480 }
481 crate::types::ActionType::MethodCall {
482 object,
483 method,
484 args,
485 } => {
486 let args_str = args
487 .iter()
488 .map(|arg| arg.to_grl())
489 .collect::<Vec<_>>()
490 .join(", ");
491 format!("{}.{}({})", object, method, args_str)
492 }
493 crate::types::ActionType::Retract { object } => {
494 format!("retract(${})", object)
495 }
496 crate::types::ActionType::Custom { action_type, .. } => {
497 format!("Custom(\"{}\")", action_type)
498 }
499 crate::types::ActionType::ActivateAgendaGroup { group } => {
500 format!("ActivateAgendaGroup(\"{}\")", group)
501 }
502 crate::types::ActionType::ScheduleRule {
503 rule_name,
504 delay_ms,
505 } => {
506 format!("ScheduleRule({}, \"{}\")", delay_ms, rule_name)
507 }
508 crate::types::ActionType::CompleteWorkflow { workflow_name } => {
509 format!("CompleteWorkflow(\"{}\")", workflow_name)
510 }
511 crate::types::ActionType::SetWorkflowData { key, value } => {
512 format!("SetWorkflowData(\"{}={}\")", key, value.to_grl())
513 }
514 crate::types::ActionType::Append { field, value } => {
515 format!("{} += {}", field, value.to_grl())
516 }
517 }
518 }
519}