1use std::collections::{HashMap, HashSet};
9use super::working_memory::{WorkingMemory, FactHandle};
10use super::network::{ReteUlNode, TypedReteUlRule};
11use super::facts::TypedFacts;
12use super::agenda::{AdvancedAgenda, Activation};
13use super::template::TemplateRegistry;
14use super::globals::GlobalsRegistry;
15use super::deffacts::DeffactsRegistry;
16
17#[derive(Debug)]
19pub struct RuleDependencyGraph {
20 fact_type_to_rules: HashMap<String, HashSet<usize>>,
22 rule_to_fact_types: HashMap<usize, HashSet<String>>,
24}
25
26impl RuleDependencyGraph {
27 pub fn new() -> Self {
29 Self {
30 fact_type_to_rules: HashMap::new(),
31 rule_to_fact_types: HashMap::new(),
32 }
33 }
34
35 pub fn add_dependency(&mut self, rule_idx: usize, fact_type: String) {
37 self.fact_type_to_rules
38 .entry(fact_type.clone())
39 .or_insert_with(HashSet::new)
40 .insert(rule_idx);
41
42 self.rule_to_fact_types
43 .entry(rule_idx)
44 .or_insert_with(HashSet::new)
45 .insert(fact_type);
46 }
47
48 pub fn get_affected_rules(&self, fact_type: &str) -> HashSet<usize> {
50 self.fact_type_to_rules
51 .get(fact_type)
52 .cloned()
53 .unwrap_or_else(HashSet::new)
54 }
55
56 pub fn get_rule_dependencies(&self, rule_idx: usize) -> HashSet<String> {
58 self.rule_to_fact_types
59 .get(&rule_idx)
60 .cloned()
61 .unwrap_or_else(HashSet::new)
62 }
63}
64
65impl Default for RuleDependencyGraph {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71pub struct IncrementalEngine {
74 working_memory: WorkingMemory,
76 rules: Vec<TypedReteUlRule>,
78 dependencies: RuleDependencyGraph,
80 agenda: AdvancedAgenda,
82 rule_matched_facts: HashMap<usize, HashSet<FactHandle>>,
84 templates: TemplateRegistry,
86 globals: GlobalsRegistry,
88 deffacts: DeffactsRegistry,
90}
91
92impl IncrementalEngine {
93 pub fn new() -> Self {
95 Self {
96 working_memory: WorkingMemory::new(),
97 rules: Vec::new(),
98 dependencies: RuleDependencyGraph::new(),
99 agenda: AdvancedAgenda::new(),
100 rule_matched_facts: HashMap::new(),
101 templates: TemplateRegistry::new(),
102 globals: GlobalsRegistry::new(),
103 deffacts: DeffactsRegistry::new(),
104 }
105 }
106
107 pub fn add_rule(&mut self, rule: TypedReteUlRule, depends_on: Vec<String>) {
109 let rule_idx = self.rules.len();
110
111 for fact_type in depends_on {
113 self.dependencies.add_dependency(rule_idx, fact_type);
114 }
115
116 self.rules.push(rule);
117 }
118
119 pub fn insert(&mut self, fact_type: String, data: TypedFacts) -> FactHandle {
121 let handle = self.working_memory.insert(fact_type.clone(), data);
122
123 self.propagate_changes_for_type(&fact_type);
125
126 handle
127 }
128
129 pub fn update(&mut self, handle: FactHandle, data: TypedFacts) -> Result<(), String> {
131 let fact_type = self.working_memory
133 .get(&handle)
134 .map(|f| f.fact_type.clone())
135 .ok_or_else(|| format!("FactHandle {} not found", handle))?;
136
137 self.working_memory.update(handle, data)?;
138
139 self.propagate_changes_for_type(&fact_type);
141
142 Ok(())
143 }
144
145 pub fn retract(&mut self, handle: FactHandle) -> Result<(), String> {
147 let fact_type = self.working_memory
149 .get(&handle)
150 .map(|f| f.fact_type.clone())
151 .ok_or_else(|| format!("FactHandle {} not found", handle))?;
152
153 self.working_memory.retract(handle)?;
154
155 self.propagate_changes_for_type(&fact_type);
157
158 Ok(())
159 }
160
161 fn propagate_changes_for_type(&mut self, fact_type: &str) {
163 let affected_rules = self.dependencies.get_affected_rules(fact_type);
165
166 if affected_rules.is_empty() {
167 return; }
169
170 let facts = self.working_memory.to_typed_facts();
172
173 for &rule_idx in &affected_rules {
175 let rule = &self.rules[rule_idx];
176
177 let matches = super::network::evaluate_rete_ul_node_typed(&rule.node, &facts);
179
180 if matches {
181 let activation = Activation::new(rule.name.clone(), rule.priority)
183 .with_no_loop(rule.no_loop);
184
185 self.agenda.add_activation(activation);
186 }
187 }
188 }
189
190 pub fn fire_all(&mut self) -> Vec<String> {
192 let mut fired_rules = Vec::new();
193
194 while let Some(activation) = self.agenda.get_next_activation() {
195 if let Some((idx, rule)) = self.rules
197 .iter_mut()
198 .enumerate()
199 .find(|(_, r)| r.name == activation.rule_name)
200 {
201 let mut facts = self.working_memory.to_typed_facts();
203 (rule.action)(&mut facts);
204
205 fired_rules.push(activation.rule_name.clone());
207 self.agenda.mark_rule_fired(&activation);
208
209 }
212 }
213
214 fired_rules
215 }
216
217 pub fn working_memory(&self) -> &WorkingMemory {
219 &self.working_memory
220 }
221
222 pub fn working_memory_mut(&mut self) -> &mut WorkingMemory {
224 &mut self.working_memory
225 }
226
227 pub fn agenda(&self) -> &AdvancedAgenda {
229 &self.agenda
230 }
231
232 pub fn agenda_mut(&mut self) -> &mut AdvancedAgenda {
234 &mut self.agenda
235 }
236
237 pub fn stats(&self) -> IncrementalEngineStats {
239 IncrementalEngineStats {
240 rules: self.rules.len(),
241 working_memory: self.working_memory.stats(),
242 agenda: self.agenda.stats(),
243 dependencies: self.dependencies.fact_type_to_rules.len(),
244 }
245 }
246
247 pub fn reset(&mut self) {
249 self.agenda.reset_fired_flags();
250 }
251
252 pub fn templates(&self) -> &TemplateRegistry {
254 &self.templates
255 }
256
257 pub fn templates_mut(&mut self) -> &mut TemplateRegistry {
259 &mut self.templates
260 }
261
262 pub fn globals(&self) -> &GlobalsRegistry {
264 &self.globals
265 }
266
267 pub fn globals_mut(&mut self) -> &mut GlobalsRegistry {
269 &mut self.globals
270 }
271
272 pub fn deffacts(&self) -> &DeffactsRegistry {
274 &self.deffacts
275 }
276
277 pub fn deffacts_mut(&mut self) -> &mut DeffactsRegistry {
279 &mut self.deffacts
280 }
281
282 pub fn load_deffacts(&mut self) -> Vec<FactHandle> {
285 let mut handles = Vec::new();
286
287 let all_facts = self.deffacts.get_all_facts();
289
290 for (_deffacts_name, fact_instance) in all_facts {
291 let handle = if self.templates.get(&fact_instance.fact_type).is_some() {
293 match self.insert_with_template(&fact_instance.fact_type, fact_instance.data) {
295 Ok(h) => h,
296 Err(_) => continue, }
298 } else {
299 self.insert(fact_instance.fact_type, fact_instance.data)
301 };
302
303 handles.push(handle);
304 }
305
306 handles
307 }
308
309 pub fn load_deffacts_by_name(&mut self, name: &str) -> crate::errors::Result<Vec<FactHandle>> {
312 let facts_to_insert = {
314 let deffacts = self.deffacts.get(name).ok_or_else(|| {
315 crate::errors::RuleEngineError::EvaluationError {
316 message: format!("Deffacts '{}' not found", name),
317 }
318 })?;
319 deffacts.facts.clone()
320 };
321
322 let mut handles = Vec::new();
323
324 for fact_instance in facts_to_insert {
325 let handle = if self.templates.get(&fact_instance.fact_type).is_some() {
327 self.insert_with_template(&fact_instance.fact_type, fact_instance.data)?
329 } else {
330 self.insert(fact_instance.fact_type, fact_instance.data)
332 };
333
334 handles.push(handle);
335 }
336
337 Ok(handles)
338 }
339
340 pub fn reset_with_deffacts(&mut self) -> Vec<FactHandle> {
343 self.working_memory = WorkingMemory::new();
345 self.agenda.clear();
346 self.rule_matched_facts.clear();
347
348 self.load_deffacts()
350 }
351
352 pub fn insert_with_template(
354 &mut self,
355 template_name: &str,
356 data: TypedFacts,
357 ) -> crate::errors::Result<FactHandle> {
358 self.templates.validate(template_name, &data)?;
360
361 Ok(self.insert(template_name.to_string(), data))
363 }
364}
365
366impl Default for IncrementalEngine {
367 fn default() -> Self {
368 Self::new()
369 }
370}
371
372#[derive(Debug)]
374pub struct IncrementalEngineStats {
375 pub rules: usize,
376 pub working_memory: super::working_memory::WorkingMemoryStats,
377 pub agenda: super::agenda::AgendaStats,
378 pub dependencies: usize,
379}
380
381impl std::fmt::Display for IncrementalEngineStats {
382 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383 write!(
384 f,
385 "Engine Stats: {} rules, {} fact types tracked\nWM: {}\nAgenda: {}",
386 self.rules,
387 self.dependencies,
388 self.working_memory,
389 self.agenda
390 )
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397 use crate::rete::network::ReteUlNode;
398 use crate::rete::alpha::AlphaNode;
399
400 #[test]
401 fn test_dependency_graph() {
402 let mut graph = RuleDependencyGraph::new();
403
404 graph.add_dependency(0, "Person".to_string());
405 graph.add_dependency(1, "Person".to_string());
406 graph.add_dependency(1, "Order".to_string());
407
408 let affected = graph.get_affected_rules("Person");
409 assert_eq!(affected.len(), 2);
410 assert!(affected.contains(&0));
411 assert!(affected.contains(&1));
412
413 let deps = graph.get_rule_dependencies(1);
414 assert_eq!(deps.len(), 2);
415 assert!(deps.contains("Person"));
416 assert!(deps.contains("Order"));
417 }
418
419 #[test]
420 fn test_incremental_propagation() {
421 let mut engine = IncrementalEngine::new();
422
423 let node = ReteUlNode::UlAlpha(AlphaNode {
425 field: "Person.age".to_string(),
426 operator: ">".to_string(),
427 value: "18".to_string(),
428 });
429
430 let rule = TypedReteUlRule {
431 name: "IsAdult".to_string(),
432 node,
433 priority: 0,
434 no_loop: true,
435 action: Box::new(|_| {}),
436 };
437
438 engine.add_rule(rule, vec!["Person".to_string()]);
439
440 let mut person = TypedFacts::new();
442 person.set("age", 25i64);
443 let handle = engine.insert("Person".to_string(), person);
444
445 let stats = engine.stats();
447 assert!(stats.agenda.total_activations > 0);
448
449 let mut updated = TypedFacts::new();
451 updated.set("age", 15i64); engine.update(handle, updated).unwrap();
453
454 }
456}