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