1use std::collections::{HashMap, HashSet, VecDeque};
35use crate::errors::{Result, RuleEngineError};
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
39pub enum ItemType {
40 Rule,
42 Template,
44 Fact,
46 All,
48}
49
50#[derive(Debug, Clone, PartialEq)]
52pub enum ExportList {
53 All,
55 None,
57 Specific(Vec<ExportItem>),
59}
60
61#[derive(Debug, Clone, PartialEq)]
63pub struct ExportItem {
64 pub item_type: ItemType,
66 pub pattern: String,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum ImportType {
73 AllRules,
75 AllTemplates,
77 Rules,
79 Templates,
81 All,
83}
84
85#[derive(Debug, Clone, PartialEq)]
87pub struct ImportDecl {
88 pub from_module: String,
90 pub import_type: ImportType,
92 pub pattern: String,
94}
95
96#[derive(Debug, Clone)]
98pub struct Module {
99 pub name: String,
101 rules: HashSet<String>,
103 templates: HashSet<String>,
105 fact_types: HashSet<String>,
107 exports: ExportList,
109 imports: Vec<ImportDecl>,
111 pub doc: Option<String>,
113}
114
115impl Module {
116 pub fn new(name: impl Into<String>) -> Self {
118 let name = name.into();
119 let exports = if name == "MAIN" {
120 ExportList::All
121 } else {
122 ExportList::None
123 };
124
125 Self {
126 name,
127 rules: HashSet::new(),
128 templates: HashSet::new(),
129 fact_types: HashSet::new(),
130 exports,
131 imports: Vec::new(),
132 doc: None,
133 }
134 }
135
136 pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
138 self.doc = Some(doc.into());
139 self
140 }
141
142 pub fn add_rule(&mut self, rule_name: impl Into<String>) {
144 self.rules.insert(rule_name.into());
145 }
146
147 pub fn add_template(&mut self, template_name: impl Into<String>) {
149 self.templates.insert(template_name.into());
150 }
151
152 pub fn add_fact_type(&mut self, fact_type: impl Into<String>) {
154 self.fact_types.insert(fact_type.into());
155 }
156
157 pub fn set_exports(&mut self, exports: ExportList) {
159 self.exports = exports;
160 }
161
162 pub fn get_exports(&self) -> &ExportList {
164 &self.exports
165 }
166
167 pub fn add_import(&mut self, import: ImportDecl) {
169 self.imports.push(import);
170 }
171
172 pub fn exports_rule(&self, rule_name: &str) -> bool {
174 match &self.exports {
175 ExportList::All => self.rules.contains(rule_name),
176 ExportList::None => false,
177 ExportList::Specific(items) => {
178 items.iter().any(|item| {
179 matches!(item.item_type, ItemType::Rule | ItemType::All)
180 && self.rules.contains(rule_name)
181 && pattern_matches(&item.pattern, rule_name)
182 })
183 }
184 }
185 }
186
187 pub fn exports_template(&self, template_name: &str) -> bool {
189 match &self.exports {
190 ExportList::All => self.templates.contains(template_name),
191 ExportList::None => false,
192 ExportList::Specific(items) => {
193 items.iter().any(|item| {
194 matches!(item.item_type, ItemType::Template | ItemType::All)
195 && self.templates.contains(template_name)
196 && pattern_matches(&item.pattern, template_name)
197 })
198 }
199 }
200 }
201
202 pub fn get_rules(&self) -> &HashSet<String> {
204 &self.rules
205 }
206
207 pub fn get_templates(&self) -> &HashSet<String> {
209 &self.templates
210 }
211
212 pub fn get_imports(&self) -> &[ImportDecl] {
214 &self.imports
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct CycleError {
221 pub cycle_path: Vec<String>,
223}
224
225impl std::fmt::Display for CycleError {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 write!(f, "Cyclic import detected: {}", self.cycle_path.join(" -> "))
228 }
229}
230
231#[derive(Debug, Clone)]
233pub struct ModuleManager {
234 modules: HashMap<String, Module>,
236 current_focus: String,
238 default_module: String,
240 import_graph: HashMap<String, HashSet<String>>,
242}
243
244impl ModuleManager {
245 pub fn new() -> Self {
247 let mut modules = HashMap::new();
248 modules.insert("MAIN".to_string(), Module::new("MAIN"));
249
250 Self {
251 modules,
252 current_focus: "MAIN".to_string(),
253 default_module: "MAIN".to_string(),
254 import_graph: HashMap::new(),
255 }
256 }
257
258 pub fn create_module(&mut self, name: impl Into<String>) -> Result<&mut Module> {
260 let name = name.into();
261
262 if self.modules.contains_key(&name) {
263 return Err(RuleEngineError::ModuleError {
264 message: format!("Module '{}' already exists", name),
265 });
266 }
267
268 self.modules.insert(name.clone(), Module::new(&name));
269 Ok(self.modules.get_mut(&name).unwrap())
270 }
271
272 pub fn get_module_mut(&mut self, name: &str) -> Result<&mut Module> {
274 self.modules.get_mut(name).ok_or_else(|| RuleEngineError::ModuleError {
275 message: format!("Module '{}' not found", name),
276 })
277 }
278
279 pub fn get_module(&self, name: &str) -> Result<&Module> {
281 self.modules.get(name).ok_or_else(|| RuleEngineError::ModuleError {
282 message: format!("Module '{}' not found", name),
283 })
284 }
285
286 pub fn delete_module(&mut self, name: &str) -> Result<()> {
288 if name == self.default_module {
289 return Err(RuleEngineError::ModuleError {
290 message: "Cannot delete default module".to_string(),
291 });
292 }
293
294 if name == self.current_focus {
295 self.current_focus = self.default_module.clone();
296 }
297
298 self.modules.remove(name).ok_or_else(|| RuleEngineError::ModuleError {
299 message: format!("Module '{}' not found", name),
300 })?;
301
302 self.import_graph.remove(name);
304 for (_, imports) in self.import_graph.iter_mut() {
305 imports.remove(name);
306 }
307
308 Ok(())
309 }
310
311 pub fn set_focus(&mut self, module_name: impl Into<String>) -> Result<()> {
313 let module_name = module_name.into();
314
315 if !self.modules.contains_key(&module_name) {
316 return Err(RuleEngineError::ModuleError {
317 message: format!("Module '{}' not found", module_name),
318 });
319 }
320
321 self.current_focus = module_name;
322 Ok(())
323 }
324
325 pub fn get_focus(&self) -> &str {
327 &self.current_focus
328 }
329
330 pub fn list_modules(&self) -> Vec<String> {
332 self.modules.keys().cloned().collect()
333 }
334
335 pub fn export_all_from(&mut self, module_name: &str, export_list: ExportList) -> Result<()> {
337 let module = self.get_module_mut(module_name)?;
338 module.set_exports(export_list);
339 Ok(())
340 }
341
342 fn detect_cycle(&self, to_module: &str, from_module: &str) -> Result<()> {
351 if to_module == from_module {
353 return Err(RuleEngineError::ModuleError {
354 message: format!(
355 "Cyclic import detected: {} cannot import from itself",
356 to_module
357 ),
358 });
359 }
360
361 let mut queue = VecDeque::new();
364 let mut visited = HashSet::new();
365 let mut parent_map: HashMap<String, String> = HashMap::new();
366
367 queue.push_back(from_module.to_string());
368 visited.insert(from_module.to_string());
369
370 while let Some(current) = queue.pop_front() {
371 if let Some(imports) = self.import_graph.get(¤t) {
373 for imported in imports {
374 if imported == to_module {
375 let mut cycle_path = vec![to_module.to_string()];
377 let mut node = current.clone();
378
379 while let Some(parent) = parent_map.get(&node) {
380 cycle_path.push(node.clone());
381 node = parent.clone();
382 }
383
384 cycle_path.push(node);
385 cycle_path.reverse();
386
387 return Err(RuleEngineError::ModuleError {
388 message: format!(
389 "Cyclic import detected: {}",
390 cycle_path.join(" -> ")
391 ),
392 });
393 }
394
395 if !visited.contains(imported) {
396 visited.insert(imported.clone());
397 parent_map.insert(imported.clone(), current.clone());
398 queue.push_back(imported.clone());
399 }
400 }
401 }
402 }
403
404 Ok(())
405 }
406
407 pub fn get_import_graph(&self) -> &HashMap<String, HashSet<String>> {
409 &self.import_graph
410 }
411
412 pub fn get_import_graph_debug(&self) -> Vec<(String, Vec<String>)> {
414 self.import_graph
415 .iter()
416 .map(|(module, imports)| {
417 (module.clone(), imports.iter().cloned().collect())
418 })
419 .collect()
420 }
421
422 pub fn import_from(
424 &mut self,
425 to_module: &str,
426 from_module: &str,
427 import_type: ImportType,
428 pattern: impl Into<String>,
429 ) -> Result<()> {
430 if !self.modules.contains_key(from_module) {
432 return Err(RuleEngineError::ModuleError {
433 message: format!("Source module '{}' not found", from_module),
434 });
435 }
436
437 self.detect_cycle(to_module, from_module)?;
439
440 let module = self.get_module_mut(to_module)?;
441 module.add_import(ImportDecl {
442 from_module: from_module.to_string(),
443 import_type,
444 pattern: pattern.into(),
445 });
446
447 self.import_graph
449 .entry(to_module.to_string())
450 .or_insert_with(HashSet::new)
451 .insert(from_module.to_string());
452
453 Ok(())
454 }
455
456 pub fn is_rule_visible(&self, rule_name: &str, to_module: &str) -> Result<bool> {
458 let module = self.get_module(to_module)?;
459
460 if module.get_rules().contains(rule_name) {
462 return Ok(true);
463 }
464
465 for import in module.get_imports() {
467 if !matches!(import.import_type, ImportType::AllRules | ImportType::Rules | ImportType::All) {
468 continue;
469 }
470
471 let from_module = self.get_module(&import.from_module)?;
472
473 if from_module.exports_rule(rule_name) && pattern_matches(&import.pattern, rule_name) {
474 return Ok(true);
475 }
476 }
477
478 Ok(false)
479 }
480
481 pub fn is_template_visible(&self, template_name: &str, to_module: &str) -> Result<bool> {
483 let module = self.get_module(to_module)?;
484
485 if module.get_templates().contains(template_name) {
487 return Ok(true);
488 }
489
490 for import in module.get_imports() {
492 if !matches!(import.import_type, ImportType::AllTemplates | ImportType::Templates | ImportType::All) {
493 continue;
494 }
495
496 let from_module = self.get_module(&import.from_module)?;
497
498 if from_module.exports_template(template_name) && pattern_matches(&import.pattern, template_name) {
499 return Ok(true);
500 }
501 }
502
503 Ok(false)
504 }
505
506 pub fn get_visible_rules(&self, module_name: &str) -> Result<Vec<String>> {
508 let module = self.get_module(module_name)?;
509 let mut visible = HashSet::new();
510
511 visible.extend(module.get_rules().iter().cloned());
513
514 for import in module.get_imports() {
516 if !matches!(import.import_type, ImportType::AllRules | ImportType::Rules | ImportType::All) {
517 continue;
518 }
519
520 let from_module = self.get_module(&import.from_module)?;
521
522 for rule in from_module.get_rules() {
523 if from_module.exports_rule(rule) && pattern_matches(&import.pattern, rule) {
524 visible.insert(rule.clone());
525 }
526 }
527 }
528
529 Ok(visible.into_iter().collect())
530 }
531
532 pub fn get_stats(&self) -> ModuleStats {
534 ModuleStats {
535 total_modules: self.modules.len(),
536 current_focus: self.current_focus.clone(),
537 modules: self.modules.iter().map(|(name, module)| {
538 (name.clone(), ModuleInfo {
539 name: name.clone(),
540 rules_count: module.rules.len(),
541 templates_count: module.templates.len(),
542 imports_count: module.imports.len(),
543 exports_type: match &module.exports {
544 ExportList::All => "All".to_string(),
545 ExportList::None => "None".to_string(),
546 ExportList::Specific(items) => format!("Specific({})", items.len()),
547 },
548 })
549 }).collect(),
550 }
551 }
552}
553
554impl Default for ModuleManager {
555 fn default() -> Self {
556 Self::new()
557 }
558}
559
560#[derive(Debug, Clone)]
562pub struct ModuleStats {
563 pub total_modules: usize,
565 pub current_focus: String,
567 pub modules: HashMap<String, ModuleInfo>,
569}
570
571#[derive(Debug, Clone)]
573pub struct ModuleInfo {
574 pub name: String,
576 pub rules_count: usize,
578 pub templates_count: usize,
580 pub imports_count: usize,
582 pub exports_type: String,
584}
585
586fn pattern_matches(pattern: &str, name: &str) -> bool {
588 if pattern == "*" || pattern == "?ALL" {
589 return true;
590 }
591
592 if pattern.ends_with('*') {
594 let prefix = &pattern[..pattern.len() - 1];
595 name.starts_with(prefix)
596 } else if pattern.starts_with('*') {
597 let suffix = &pattern[1..];
598 name.ends_with(suffix)
599 } else {
600 pattern == name
601 }
602}
603
604#[cfg(test)]
605mod tests {
606 use super::*;
607
608 #[test]
609 fn test_module_creation() {
610 let mut manager = ModuleManager::new();
611
612 assert!(manager.create_module("TEST").is_ok());
613 assert!(manager.create_module("TEST").is_err()); assert_eq!(manager.list_modules().len(), 2); }
617
618 #[test]
619 fn test_module_focus() {
620 let mut manager = ModuleManager::new();
621 manager.create_module("SENSORS").unwrap();
622
623 assert_eq!(manager.get_focus(), "MAIN");
624
625 manager.set_focus("SENSORS").unwrap();
626 assert_eq!(manager.get_focus(), "SENSORS");
627
628 assert!(manager.set_focus("NONEXISTENT").is_err());
629 }
630
631 #[test]
632 fn test_export_import() {
633 let mut manager = ModuleManager::new();
634 manager.create_module("SENSORS").unwrap();
635 manager.create_module("CONTROL").unwrap();
636
637 let sensors = manager.get_module_mut("SENSORS").unwrap();
639 sensors.add_rule("sensor-temp");
640 sensors.add_rule("sensor-pressure");
641 sensors.set_exports(ExportList::Specific(vec![
642 ExportItem {
643 item_type: ItemType::Rule,
644 pattern: "sensor-*".to_string(),
645 },
646 ]));
647
648 manager.import_from("CONTROL", "SENSORS", ImportType::AllRules, "*").unwrap();
650
651 assert!(manager.is_rule_visible("sensor-temp", "CONTROL").unwrap());
653 assert!(manager.is_rule_visible("sensor-pressure", "CONTROL").unwrap());
654 }
655
656 #[test]
657 fn test_pattern_matching() {
658 assert!(pattern_matches("*", "anything"));
659 assert!(pattern_matches("sensor-*", "sensor-temp"));
660 assert!(pattern_matches("sensor-*", "sensor-pressure"));
661 assert!(!pattern_matches("sensor-*", "control-temp"));
662 assert!(pattern_matches("*-temp", "sensor-temp"));
663 assert!(pattern_matches("exact", "exact"));
664 assert!(!pattern_matches("exact", "not-exact"));
665 }
666
667 #[test]
668 fn test_main_module_default_export() {
669 let manager = ModuleManager::new();
670 let main_module = manager.get_module("MAIN").unwrap();
671
672 assert!(matches!(main_module.exports, ExportList::All));
674 }
675
676 #[test]
677 fn test_user_module_default_export() {
678 let mut manager = ModuleManager::new();
679 manager.create_module("USER").unwrap();
680 let user_module = manager.get_module("USER").unwrap();
681
682 assert!(matches!(user_module.exports, ExportList::None));
684 }
685
686 #[test]
687 fn test_visibility_own_rules() {
688 let mut manager = ModuleManager::new();
689 manager.create_module("TEST").unwrap();
690
691 let test_module = manager.get_module_mut("TEST").unwrap();
692 test_module.add_rule("my-rule");
693
694 assert!(manager.is_rule_visible("my-rule", "TEST").unwrap());
696 }
697
698 #[test]
699 fn test_get_visible_rules() {
700 let mut manager = ModuleManager::new();
701 manager.create_module("MOD1").unwrap();
702 manager.create_module("MOD2").unwrap();
703
704 let mod1 = manager.get_module_mut("MOD1").unwrap();
706 mod1.add_rule("rule1");
707 mod1.add_rule("rule2");
708 mod1.set_exports(ExportList::All);
709
710 let mod2 = manager.get_module_mut("MOD2").unwrap();
712 mod2.add_rule("rule3");
713
714 manager.import_from("MOD2", "MOD1", ImportType::AllRules, "*").unwrap();
716
717 let visible = manager.get_visible_rules("MOD2").unwrap();
718 assert!(visible.contains(&"rule1".to_string()));
719 assert!(visible.contains(&"rule2".to_string()));
720 assert!(visible.contains(&"rule3".to_string()));
721 assert_eq!(visible.len(), 3);
722 }
723
724 #[test]
725 fn test_module_stats() {
726 let mut manager = ModuleManager::new();
727 manager.create_module("TEST").unwrap();
728
729 let test_module = manager.get_module_mut("TEST").unwrap();
730 test_module.add_rule("rule1");
731 test_module.add_template("template1");
732
733 let stats = manager.get_stats();
734 assert_eq!(stats.total_modules, 2); assert_eq!(stats.current_focus, "MAIN");
736
737 let test_info = stats.modules.get("TEST").unwrap();
738 assert_eq!(test_info.rules_count, 1);
739 assert_eq!(test_info.templates_count, 1);
740 }
741}