1use super::agenda::{Activation, AdvancedAgenda};
9use super::deffacts::DeffactsRegistry;
10use super::facts::{FactValue, TypedFacts};
11use super::globals::GlobalsRegistry;
12use super::network::TypedReteUlRule;
13use super::template::TemplateRegistry;
14use super::tms::TruthMaintenanceSystem;
15use super::working_memory::{FactHandle, WorkingMemory};
16use crate::errors::{Result, RuleEngineError};
17use std::collections::{HashMap, HashSet};
18use std::sync::Arc;
19
20#[derive(Debug)]
22pub struct RuleDependencyGraph {
23 fact_type_to_rules: HashMap<String, HashSet<usize>>,
25 rule_to_fact_types: HashMap<usize, HashSet<String>>,
27}
28
29impl RuleDependencyGraph {
30 pub fn new() -> Self {
32 Self {
33 fact_type_to_rules: HashMap::new(),
34 rule_to_fact_types: HashMap::new(),
35 }
36 }
37
38 pub fn add_dependency(&mut self, rule_idx: usize, fact_type: String) {
40 self.fact_type_to_rules
41 .entry(fact_type.clone())
42 .or_default()
43 .insert(rule_idx);
44
45 self.rule_to_fact_types
46 .entry(rule_idx)
47 .or_default()
48 .insert(fact_type);
49 }
50
51 pub fn get_affected_rules(&self, fact_type: &str) -> HashSet<usize> {
53 self.fact_type_to_rules
54 .get(fact_type)
55 .cloned()
56 .unwrap_or_else(HashSet::new)
57 }
58
59 pub fn get_rule_dependencies(&self, rule_idx: usize) -> HashSet<String> {
61 self.rule_to_fact_types
62 .get(&rule_idx)
63 .cloned()
64 .unwrap_or_else(HashSet::new)
65 }
66}
67
68impl Default for RuleDependencyGraph {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74pub type ReteCustomFunction =
77 Arc<dyn Fn(&[FactValue], &TypedFacts) -> Result<FactValue> + Send + Sync>;
78
79pub struct IncrementalEngine {
82 working_memory: WorkingMemory,
84 rules: Vec<TypedReteUlRule>,
86 dependencies: RuleDependencyGraph,
88 agenda: AdvancedAgenda,
90 rule_matched_facts: HashMap<usize, HashSet<FactHandle>>,
92 templates: TemplateRegistry,
94 globals: GlobalsRegistry,
96 deffacts: DeffactsRegistry,
98 custom_functions: HashMap<String, ReteCustomFunction>,
100 tms: TruthMaintenanceSystem,
102}
103
104impl IncrementalEngine {
105 pub fn new() -> Self {
107 Self {
108 working_memory: WorkingMemory::new(),
109 rules: Vec::new(),
110 dependencies: RuleDependencyGraph::new(),
111 agenda: AdvancedAgenda::new(),
112 rule_matched_facts: HashMap::new(),
113 custom_functions: HashMap::new(),
114 templates: TemplateRegistry::new(),
115 globals: GlobalsRegistry::new(),
116 deffacts: DeffactsRegistry::new(),
117 tms: TruthMaintenanceSystem::new(),
118 }
119 }
120
121 pub fn add_rule(&mut self, rule: TypedReteUlRule, depends_on: Vec<String>) {
123 let rule_idx = self.rules.len();
124
125 for fact_type in depends_on {
127 self.dependencies.add_dependency(rule_idx, fact_type);
128 }
129
130 self.rules.push(rule);
131 }
132
133 pub fn insert(&mut self, fact_type: String, data: TypedFacts) -> FactHandle {
135 let handle = self.working_memory.insert(fact_type.clone(), data);
136
137 self.tms.add_explicit_justification(handle);
139
140 self.propagate_changes_for_type(&fact_type);
142
143 handle
144 }
145
146 pub fn update(&mut self, handle: FactHandle, data: TypedFacts) -> Result<()> {
148 let fact_type = self
150 .working_memory
151 .get(&handle)
152 .map(|f| f.fact_type.clone())
153 .ok_or_else(|| RuleEngineError::FieldNotFound {
154 field: format!("FactHandle {} not found", handle),
155 })?;
156
157 self.working_memory
158 .update(handle, data)
159 .map_err(|e| RuleEngineError::EvaluationError { message: e })?;
160
161 self.propagate_changes_for_type(&fact_type);
163
164 Ok(())
165 }
166
167 pub fn retract(&mut self, handle: FactHandle) -> Result<()> {
169 let fact_type = self
171 .working_memory
172 .get(&handle)
173 .map(|f| f.fact_type.clone())
174 .ok_or_else(|| RuleEngineError::FieldNotFound {
175 field: format!("FactHandle {} not found", handle),
176 })?;
177
178 self.working_memory
179 .retract(handle)
180 .map_err(|e| RuleEngineError::EvaluationError { message: e })?;
181
182 let cascaded_facts = self.tms.retract_with_cascade(handle);
184
185 for cascaded_handle in cascaded_facts {
187 if let Ok(fact_type) = self
188 .working_memory
189 .get(&cascaded_handle)
190 .map(|f| f.fact_type.clone())
191 .ok_or_else(|| RuleEngineError::FieldNotFound {
192 field: format!("FactHandle {} not found", cascaded_handle),
193 })
194 {
195 let _ = self.working_memory.retract(cascaded_handle);
196 self.propagate_changes_for_type(&fact_type);
198 }
199 }
200
201 self.propagate_changes_for_type(&fact_type);
203
204 Ok(())
205 }
206
207 pub fn insert_explicit(&mut self, fact_type: String, data: TypedFacts) -> FactHandle {
210 let handle = self.working_memory.insert(fact_type.clone(), data);
211
212 self.tms.add_explicit_justification(handle);
214
215 self.propagate_changes_for_type(&fact_type);
217
218 handle
219 }
220
221 pub fn insert_logical(
230 &mut self,
231 fact_type: String,
232 data: TypedFacts,
233 source_rule: String,
234 premise_handles: Vec<FactHandle>,
235 ) -> FactHandle {
236 let handle = self.working_memory.insert(fact_type.clone(), data);
237
238 self.tms
240 .add_logical_justification(handle, source_rule, premise_handles);
241
242 self.propagate_changes_for_type(&fact_type);
244
245 handle
246 }
247
248 pub fn resolve_premise_keys(&self, premise_keys: Vec<String>) -> Vec<FactHandle> {
253 let mut handles = Vec::new();
254
255 for key in premise_keys {
256 if let Some(eq_pos) = key.find('=') {
258 let left = &key[..eq_pos];
259 let value_part = &key[eq_pos + 1..];
260
261 if let Some(dot_pos) = left.find('.') {
262 let fact_type = &left[..dot_pos];
263 let field = &left[dot_pos + 1..];
264
265 let facts = self.working_memory.get_by_type(fact_type);
267 if value_part.is_empty() {
269 if let Some(fact) = facts.iter().rev().find(|f| !f.metadata.retracted) {
271 handles.push(fact.handle);
272 continue;
273 }
274 } else {
275 fn parse_literal(s: &str) -> super::facts::FactValue {
279 let s = s.trim();
280 if s == "true" {
281 return super::facts::FactValue::Boolean(true);
282 }
283 if s == "false" {
284 return super::facts::FactValue::Boolean(false);
285 }
286 if (s.starts_with('"') && s.ends_with('"'))
288 || (s.starts_with('\'') && s.ends_with('\''))
289 {
290 return super::facts::FactValue::String(
291 s[1..s.len() - 1].to_string(),
292 );
293 }
294 if let Ok(i) = s.parse::<i64>() {
296 return super::facts::FactValue::Integer(i);
297 }
298 if let Ok(f) = s.parse::<f64>() {
300 return super::facts::FactValue::Float(f);
301 }
302
303 super::facts::FactValue::String(s.to_string())
305 }
306
307 fn fact_value_equal(
308 a: &super::facts::FactValue,
309 b: &super::facts::FactValue,
310 ) -> bool {
311 use super::facts::FactValue;
312 match (a, b) {
313 (FactValue::Boolean(x), FactValue::Boolean(y)) => x == y,
314 (FactValue::Integer(x), FactValue::Integer(y)) => x == y,
315 (FactValue::Float(x), FactValue::Float(y)) => (x - y).abs() < 1e-9,
316 (FactValue::Integer(x), FactValue::Float(y)) => {
318 ((*x as f64) - *y).abs() < 1e-9
319 }
320 (FactValue::Float(x), FactValue::Integer(y)) => {
321 (*x - (*y as f64)).abs() < 1e-9
322 }
323 (FactValue::String(x), FactValue::String(y)) => x == y,
324 _ => a.as_str() == b.as_str(),
326 }
327 }
328
329 let expected = parse_literal(value_part);
330
331 if let Some(fact) = facts.iter().rev().find(|fact| {
333 if fact.metadata.retracted {
334 return false;
335 }
336 if let Some(fv) = fact.data.get(field) {
337 fact_value_equal(fv, &expected) || fv.as_str() == value_part
338 } else {
339 false
340 }
341 }) {
342 handles.push(fact.handle);
343 }
344 }
345 }
346 }
347 }
348
349 handles
350 }
351
352 pub fn tms(&self) -> &TruthMaintenanceSystem {
354 &self.tms
355 }
356
357 pub fn tms_mut(&mut self) -> &mut TruthMaintenanceSystem {
359 &mut self.tms
360 }
361
362 fn propagate_changes_for_type(&mut self, fact_type: &str) {
364 let affected_rules = self.dependencies.get_affected_rules(fact_type);
366
367 if affected_rules.is_empty() {
368 return; }
370
371 let facts_of_type = self.working_memory.get_by_type(fact_type);
373
374 for &rule_idx in &affected_rules {
376 let rule = &self.rules[rule_idx];
377
378 for fact in &facts_of_type {
380 let mut single_fact_data = TypedFacts::new();
382 for (key, value) in fact.data.get_all() {
383 single_fact_data.set(format!("{}.{}", fact_type, key), value.clone());
384 }
385 single_fact_data.set_fact_handle(fact_type.to_string(), fact.handle);
387
388 let matches = super::network::evaluate_rete_ul_node_typed(
390 &rule.node,
391 &single_fact_data,
392 &self.custom_functions,
393 );
394
395 if matches {
396 let activation = Activation::new(rule.name.clone(), rule.priority)
398 .with_no_loop(rule.no_loop)
399 .with_matched_fact(fact.handle);
400
401 self.agenda.add_activation(activation);
402 }
403 }
404 }
405 }
406
407 fn propagate_changes(&mut self) {
409 let fact_types: Vec<String> = self
411 .working_memory
412 .get_all_facts()
413 .iter()
414 .map(|f| f.fact_type.clone())
415 .collect::<std::collections::HashSet<_>>()
416 .into_iter()
417 .collect();
418
419 for fact_type in fact_types {
421 let facts_of_type = self.working_memory.get_by_type(&fact_type);
422
423 for rule in self.rules.iter() {
424 if rule.no_loop && self.agenda.has_fired(&rule.name) {
426 continue;
427 }
428
429 for fact in &facts_of_type {
431 let mut single_fact_data = TypedFacts::new();
432 for (key, value) in fact.data.get_all() {
433 single_fact_data.set(format!("{}.{}", fact_type, key), value.clone());
434 }
435
436 let matches = super::network::evaluate_rete_ul_node_typed(
437 &rule.node,
438 &single_fact_data,
439 &self.custom_functions,
440 );
441
442 if matches {
443 let activation = Activation::new(rule.name.clone(), rule.priority)
444 .with_no_loop(rule.no_loop)
445 .with_matched_fact(fact.handle);
446
447 self.agenda.add_activation(activation);
448 }
449 }
450 }
451 }
452 }
453
454 pub fn fire_all(&mut self) -> Vec<String> {
456 let mut fired_rules = Vec::new();
457 let max_iterations = 1000; let mut iteration_count = 0;
459
460 while let Some(activation) = self.agenda.get_next_activation() {
461 iteration_count += 1;
462 if iteration_count > max_iterations {
463 eprintln!("WARNING: Maximum iterations ({}) reached in fire_all(). Possible infinite loop!", max_iterations);
464 break;
465 }
466
467 if let Some((_idx, rule)) = self
469 .rules
470 .iter_mut()
471 .enumerate()
472 .find(|(_, r)| r.name == activation.rule_name)
473 {
474 if let Some(matched_handle) = activation.matched_fact_handle {
476 if self.working_memory.get(&matched_handle).is_none() {
477 continue;
479 }
480 }
481
482 let original_facts = self.working_memory.to_typed_facts();
484 let mut modified_facts = original_facts.clone();
485
486 if let Some(matched_handle) = activation.matched_fact_handle {
488 if let Some(fact) = self.working_memory.get(&matched_handle) {
490 modified_facts.set_fact_handle(fact.fact_type.clone(), matched_handle);
491 }
492 }
493
494 let mut action_results = super::ActionResults::new();
495 (rule.action)(&mut modified_facts, &mut action_results);
496
497 let mut updates_by_type: HashMap<String, Vec<(String, FactValue)>> = HashMap::new();
500
501 for (key, value) in modified_facts.get_all() {
502 if let Some(original_value) = original_facts.get(key) {
505 if original_value != value {
506 let parts: Vec<&str> = key.split('.').collect();
508 if parts.len() >= 2 {
509 let fact_type = parts[0].to_string();
510 let field = if parts.len() == 2 {
512 parts[1].to_string()
513 } else {
514 parts[parts.len() - 1].to_string()
515 };
516
517 updates_by_type
518 .entry(fact_type)
519 .or_default()
520 .push((field, value.clone()));
521 }
522 }
523 } else {
524 let parts: Vec<&str> = key.split('.').collect();
526 if parts.len() >= 2 {
527 let fact_type = parts[0].to_string();
528 let field = if parts.len() == 2 {
529 parts[1].to_string()
530 } else {
531 parts[parts.len() - 1].to_string()
532 };
533
534 updates_by_type
535 .entry(fact_type)
536 .or_default()
537 .push((field, value.clone()));
538 }
539 }
540 }
541
542 for (fact_type, field_updates) in updates_by_type {
544 let fact_handles: Vec<FactHandle> = self
546 .working_memory
547 .get_by_type(&fact_type)
548 .iter()
549 .map(|f| f.handle)
550 .collect();
551
552 for handle in fact_handles {
553 if let Some(fact) = self.working_memory.get(&handle) {
554 let mut updated_data = fact.data.clone();
555
556 for (field, value) in &field_updates {
558 updated_data.set(field, value.clone());
559 }
560
561 let _ = self.working_memory.update(handle, updated_data);
562 }
563 }
564 }
565
566 self.propagate_changes();
569
570 self.process_action_results(action_results);
572
573 fired_rules.push(activation.rule_name.clone());
575 self.agenda.mark_rule_fired(&activation);
576 }
577 }
578
579 fired_rules
580 }
581
582 fn process_action_results(&mut self, results: super::ActionResults) {
584 for result in results.results {
585 match result {
586 super::ActionResult::Retract(handle) => {
587 if let Err(e) = self.retract(handle) {
589 eprintln!("❌ Failed to retract fact {:?}: {}", handle, e);
590 }
591 }
592 super::ActionResult::RetractByType(fact_type) => {
593 let facts_of_type = self.working_memory.get_by_type(&fact_type);
595 if let Some(fact) = facts_of_type.first() {
596 let handle = fact.handle;
597 if let Err(e) = self.retract(handle) {
598 eprintln!("❌ Failed to retract fact {:?}: {}", handle, e);
599 }
600 }
601 }
602 super::ActionResult::Update(handle) => {
603 if let Some(fact) = self.working_memory.get(&handle) {
605 let fact_type = fact.fact_type.clone();
606 self.propagate_changes_for_type(&fact_type);
607 }
608 }
609 super::ActionResult::ActivateAgendaGroup(group) => {
610 self.agenda.set_focus(group);
612 }
613 super::ActionResult::InsertFact { fact_type, data } => {
614 self.insert_explicit(fact_type, data);
616 }
617 super::ActionResult::InsertLogicalFact {
618 fact_type,
619 data,
620 rule_name,
621 premises,
622 } => {
623 let _handle = self.insert_logical(fact_type, data, rule_name, premises);
625 }
626 super::ActionResult::CallFunction {
627 function_name,
628 args,
629 } => {
630 if let Some(func) = self.custom_functions.get(&function_name) {
632 let fact_values: Vec<FactValue> =
634 args.iter().map(|s| FactValue::String(s.clone())).collect();
635
636 let all_facts = self.working_memory.to_typed_facts();
638 match func(&fact_values, &all_facts) {
639 Ok(_) => println!("✅ Called function: {}", function_name),
640 Err(e) => eprintln!("❌ Function {} failed: {}", function_name, e),
641 }
642 } else {
643 println!("🔧 Function call queued: {}({:?})", function_name, args);
645 }
646 }
647 super::ActionResult::ScheduleRule {
648 rule_name,
649 delay_ms,
650 } => {
651 println!("⏰ Rule scheduled: {} after {}ms", rule_name, delay_ms);
653 }
655 super::ActionResult::None => {
656 }
658 }
659 }
660 }
661
662 pub fn working_memory(&self) -> &WorkingMemory {
664 &self.working_memory
665 }
666
667 pub fn working_memory_mut(&mut self) -> &mut WorkingMemory {
669 &mut self.working_memory
670 }
671
672 pub fn agenda(&self) -> &AdvancedAgenda {
674 &self.agenda
675 }
676
677 pub fn agenda_mut(&mut self) -> &mut AdvancedAgenda {
679 &mut self.agenda
680 }
681
682 pub fn set_conflict_resolution_strategy(
687 &mut self,
688 strategy: super::agenda::ConflictResolutionStrategy,
689 ) {
690 self.agenda.set_strategy(strategy);
691 }
692
693 pub fn conflict_resolution_strategy(&self) -> super::agenda::ConflictResolutionStrategy {
695 self.agenda.strategy()
696 }
697
698 pub fn stats(&self) -> IncrementalEngineStats {
700 IncrementalEngineStats {
701 rules: self.rules.len(),
702 working_memory: self.working_memory.stats(),
703 agenda: self.agenda.stats(),
704 dependencies: self.dependencies.fact_type_to_rules.len(),
705 }
706 }
707
708 pub fn reset(&mut self) {
710 self.agenda.reset_fired_flags();
711 }
712
713 pub fn templates(&self) -> &TemplateRegistry {
715 &self.templates
716 }
717
718 pub fn templates_mut(&mut self) -> &mut TemplateRegistry {
720 &mut self.templates
721 }
722
723 pub fn register_function<F>(&mut self, name: &str, func: F)
742 where
743 F: Fn(&[FactValue], &TypedFacts) -> Result<FactValue> + Send + Sync + 'static,
744 {
745 self.custom_functions
746 .insert(name.to_string(), Arc::new(func));
747 }
748
749 pub fn get_function(&self, name: &str) -> Option<&ReteCustomFunction> {
751 self.custom_functions.get(name)
752 }
753
754 pub fn globals(&self) -> &GlobalsRegistry {
756 &self.globals
757 }
758
759 pub fn globals_mut(&mut self) -> &mut GlobalsRegistry {
761 &mut self.globals
762 }
763
764 pub fn deffacts(&self) -> &DeffactsRegistry {
766 &self.deffacts
767 }
768
769 pub fn deffacts_mut(&mut self) -> &mut DeffactsRegistry {
771 &mut self.deffacts
772 }
773
774 pub fn load_deffacts(&mut self) -> Vec<FactHandle> {
777 let mut handles = Vec::new();
778
779 let all_facts = self.deffacts.get_all_facts();
781
782 for (_deffacts_name, fact_instance) in all_facts {
783 let handle = if self.templates.get(&fact_instance.fact_type).is_some() {
785 match self.insert_with_template(&fact_instance.fact_type, fact_instance.data) {
787 Ok(h) => h,
788 Err(_) => continue, }
790 } else {
791 self.insert(fact_instance.fact_type, fact_instance.data)
793 };
794
795 handles.push(handle);
796 }
797
798 handles
799 }
800
801 pub fn load_deffacts_by_name(&mut self, name: &str) -> crate::errors::Result<Vec<FactHandle>> {
804 let facts_to_insert = {
806 let deffacts = self.deffacts.get(name).ok_or_else(|| {
807 crate::errors::RuleEngineError::EvaluationError {
808 message: format!("Deffacts '{}' not found", name),
809 }
810 })?;
811 deffacts.facts.clone()
812 };
813
814 let mut handles = Vec::new();
815
816 for fact_instance in facts_to_insert {
817 let handle = if self.templates.get(&fact_instance.fact_type).is_some() {
819 self.insert_with_template(&fact_instance.fact_type, fact_instance.data)?
821 } else {
822 self.insert(fact_instance.fact_type, fact_instance.data)
824 };
825
826 handles.push(handle);
827 }
828
829 Ok(handles)
830 }
831
832 pub fn reset_with_deffacts(&mut self) -> Vec<FactHandle> {
835 self.working_memory = WorkingMemory::new();
837 self.agenda.clear();
838 self.rule_matched_facts.clear();
839
840 self.load_deffacts()
842 }
843
844 pub fn insert_with_template(
846 &mut self,
847 template_name: &str,
848 data: TypedFacts,
849 ) -> crate::errors::Result<FactHandle> {
850 self.templates.validate(template_name, &data)?;
852
853 Ok(self.insert(template_name.to_string(), data))
855 }
856}
857
858impl Default for IncrementalEngine {
859 fn default() -> Self {
860 Self::new()
861 }
862}
863
864#[derive(Debug)]
866pub struct IncrementalEngineStats {
867 pub rules: usize,
868 pub working_memory: super::working_memory::WorkingMemoryStats,
869 pub agenda: super::agenda::AgendaStats,
870 pub dependencies: usize,
871}
872
873impl std::fmt::Display for IncrementalEngineStats {
874 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
875 write!(
876 f,
877 "Engine Stats: {} rules, {} fact types tracked\nWM: {}\nAgenda: {}",
878 self.rules, self.dependencies, self.working_memory, self.agenda
879 )
880 }
881}
882
883#[cfg(test)]
884mod tests {
885 use super::*;
886 use crate::rete::alpha::AlphaNode;
887 use crate::rete::network::ReteUlNode;
888
889 #[test]
890 fn test_dependency_graph() {
891 let mut graph = RuleDependencyGraph::new();
892
893 graph.add_dependency(0, "Person".to_string());
894 graph.add_dependency(1, "Person".to_string());
895 graph.add_dependency(1, "Order".to_string());
896
897 let affected = graph.get_affected_rules("Person");
898 assert_eq!(affected.len(), 2);
899 assert!(affected.contains(&0));
900 assert!(affected.contains(&1));
901
902 let deps = graph.get_rule_dependencies(1);
903 assert_eq!(deps.len(), 2);
904 assert!(deps.contains("Person"));
905 assert!(deps.contains("Order"));
906 }
907
908 #[test]
909 fn test_incremental_propagation() {
910 let mut engine = IncrementalEngine::new();
911
912 let node = ReteUlNode::UlAlpha(AlphaNode {
914 field: "Person.age".to_string(),
915 operator: ">".to_string(),
916 value: "18".to_string(),
917 });
918
919 let rule = TypedReteUlRule {
920 name: "IsAdult".to_string(),
921 node,
922 priority: 0,
923 no_loop: true,
924 action: std::sync::Arc::new(|_, _| {}),
925 };
926
927 engine.add_rule(rule, vec!["Person".to_string()]);
928
929 let mut person = TypedFacts::new();
931 person.set("age", 25i64);
932 let handle = engine.insert("Person".to_string(), person);
933
934 let stats = engine.stats();
936 assert!(stats.agenda.total_activations > 0);
937
938 let mut updated = TypedFacts::new();
940 updated.set("age", 15i64); engine.update(handle, updated).unwrap();
942
943 }
945}