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