1use std::collections::{HashMap, HashSet};
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 ModuleManager {
221 modules: HashMap<String, Module>,
223 current_focus: String,
225 default_module: String,
227}
228
229impl ModuleManager {
230 pub fn new() -> Self {
232 let mut modules = HashMap::new();
233 modules.insert("MAIN".to_string(), Module::new("MAIN"));
234
235 Self {
236 modules,
237 current_focus: "MAIN".to_string(),
238 default_module: "MAIN".to_string(),
239 }
240 }
241
242 pub fn create_module(&mut self, name: impl Into<String>) -> Result<&mut Module> {
244 let name = name.into();
245
246 if self.modules.contains_key(&name) {
247 return Err(RuleEngineError::ModuleError {
248 message: format!("Module '{}' already exists", name),
249 });
250 }
251
252 self.modules.insert(name.clone(), Module::new(&name));
253 Ok(self.modules.get_mut(&name).unwrap())
254 }
255
256 pub fn get_module_mut(&mut self, name: &str) -> Result<&mut Module> {
258 self.modules.get_mut(name).ok_or_else(|| RuleEngineError::ModuleError {
259 message: format!("Module '{}' not found", name),
260 })
261 }
262
263 pub fn get_module(&self, name: &str) -> Result<&Module> {
265 self.modules.get(name).ok_or_else(|| RuleEngineError::ModuleError {
266 message: format!("Module '{}' not found", name),
267 })
268 }
269
270 pub fn delete_module(&mut self, name: &str) -> Result<()> {
272 if name == self.default_module {
273 return Err(RuleEngineError::ModuleError {
274 message: "Cannot delete default module".to_string(),
275 });
276 }
277
278 if name == self.current_focus {
279 self.current_focus = self.default_module.clone();
280 }
281
282 self.modules.remove(name).ok_or_else(|| RuleEngineError::ModuleError {
283 message: format!("Module '{}' not found", name),
284 })?;
285
286 Ok(())
287 }
288
289 pub fn set_focus(&mut self, module_name: impl Into<String>) -> Result<()> {
291 let module_name = module_name.into();
292
293 if !self.modules.contains_key(&module_name) {
294 return Err(RuleEngineError::ModuleError {
295 message: format!("Module '{}' not found", module_name),
296 });
297 }
298
299 self.current_focus = module_name;
300 Ok(())
301 }
302
303 pub fn get_focus(&self) -> &str {
305 &self.current_focus
306 }
307
308 pub fn list_modules(&self) -> Vec<String> {
310 self.modules.keys().cloned().collect()
311 }
312
313 pub fn export_all_from(&mut self, module_name: &str, export_list: ExportList) -> Result<()> {
315 let module = self.get_module_mut(module_name)?;
316 module.set_exports(export_list);
317 Ok(())
318 }
319
320 pub fn import_from(
322 &mut self,
323 to_module: &str,
324 from_module: &str,
325 import_type: ImportType,
326 pattern: impl Into<String>,
327 ) -> Result<()> {
328 if !self.modules.contains_key(from_module) {
330 return Err(RuleEngineError::ModuleError {
331 message: format!("Source module '{}' not found", from_module),
332 });
333 }
334
335 let module = self.get_module_mut(to_module)?;
336 module.add_import(ImportDecl {
337 from_module: from_module.to_string(),
338 import_type,
339 pattern: pattern.into(),
340 });
341
342 Ok(())
343 }
344
345 pub fn is_rule_visible(&self, rule_name: &str, to_module: &str) -> Result<bool> {
347 let module = self.get_module(to_module)?;
348
349 if module.get_rules().contains(rule_name) {
351 return Ok(true);
352 }
353
354 for import in module.get_imports() {
356 if !matches!(import.import_type, ImportType::AllRules | ImportType::Rules | ImportType::All) {
357 continue;
358 }
359
360 let from_module = self.get_module(&import.from_module)?;
361
362 if from_module.exports_rule(rule_name) && pattern_matches(&import.pattern, rule_name) {
363 return Ok(true);
364 }
365 }
366
367 Ok(false)
368 }
369
370 pub fn is_template_visible(&self, template_name: &str, to_module: &str) -> Result<bool> {
372 let module = self.get_module(to_module)?;
373
374 if module.get_templates().contains(template_name) {
376 return Ok(true);
377 }
378
379 for import in module.get_imports() {
381 if !matches!(import.import_type, ImportType::AllTemplates | ImportType::Templates | ImportType::All) {
382 continue;
383 }
384
385 let from_module = self.get_module(&import.from_module)?;
386
387 if from_module.exports_template(template_name) && pattern_matches(&import.pattern, template_name) {
388 return Ok(true);
389 }
390 }
391
392 Ok(false)
393 }
394
395 pub fn get_visible_rules(&self, module_name: &str) -> Result<Vec<String>> {
397 let module = self.get_module(module_name)?;
398 let mut visible = HashSet::new();
399
400 visible.extend(module.get_rules().iter().cloned());
402
403 for import in module.get_imports() {
405 if !matches!(import.import_type, ImportType::AllRules | ImportType::Rules | ImportType::All) {
406 continue;
407 }
408
409 let from_module = self.get_module(&import.from_module)?;
410
411 for rule in from_module.get_rules() {
412 if from_module.exports_rule(rule) && pattern_matches(&import.pattern, rule) {
413 visible.insert(rule.clone());
414 }
415 }
416 }
417
418 Ok(visible.into_iter().collect())
419 }
420
421 pub fn get_stats(&self) -> ModuleStats {
423 ModuleStats {
424 total_modules: self.modules.len(),
425 current_focus: self.current_focus.clone(),
426 modules: self.modules.iter().map(|(name, module)| {
427 (name.clone(), ModuleInfo {
428 name: name.clone(),
429 rules_count: module.rules.len(),
430 templates_count: module.templates.len(),
431 imports_count: module.imports.len(),
432 exports_type: match &module.exports {
433 ExportList::All => "All".to_string(),
434 ExportList::None => "None".to_string(),
435 ExportList::Specific(items) => format!("Specific({})", items.len()),
436 },
437 })
438 }).collect(),
439 }
440 }
441}
442
443impl Default for ModuleManager {
444 fn default() -> Self {
445 Self::new()
446 }
447}
448
449#[derive(Debug, Clone)]
451pub struct ModuleStats {
452 pub total_modules: usize,
454 pub current_focus: String,
456 pub modules: HashMap<String, ModuleInfo>,
458}
459
460#[derive(Debug, Clone)]
462pub struct ModuleInfo {
463 pub name: String,
465 pub rules_count: usize,
467 pub templates_count: usize,
469 pub imports_count: usize,
471 pub exports_type: String,
473}
474
475fn pattern_matches(pattern: &str, name: &str) -> bool {
477 if pattern == "*" || pattern == "?ALL" {
478 return true;
479 }
480
481 if pattern.ends_with('*') {
483 let prefix = &pattern[..pattern.len() - 1];
484 name.starts_with(prefix)
485 } else if pattern.starts_with('*') {
486 let suffix = &pattern[1..];
487 name.ends_with(suffix)
488 } else {
489 pattern == name
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_module_creation() {
499 let mut manager = ModuleManager::new();
500
501 assert!(manager.create_module("TEST").is_ok());
502 assert!(manager.create_module("TEST").is_err()); assert_eq!(manager.list_modules().len(), 2); }
506
507 #[test]
508 fn test_module_focus() {
509 let mut manager = ModuleManager::new();
510 manager.create_module("SENSORS").unwrap();
511
512 assert_eq!(manager.get_focus(), "MAIN");
513
514 manager.set_focus("SENSORS").unwrap();
515 assert_eq!(manager.get_focus(), "SENSORS");
516
517 assert!(manager.set_focus("NONEXISTENT").is_err());
518 }
519
520 #[test]
521 fn test_export_import() {
522 let mut manager = ModuleManager::new();
523 manager.create_module("SENSORS").unwrap();
524 manager.create_module("CONTROL").unwrap();
525
526 let sensors = manager.get_module_mut("SENSORS").unwrap();
528 sensors.add_rule("sensor-temp");
529 sensors.add_rule("sensor-pressure");
530 sensors.set_exports(ExportList::Specific(vec![
531 ExportItem {
532 item_type: ItemType::Rule,
533 pattern: "sensor-*".to_string(),
534 },
535 ]));
536
537 manager.import_from("CONTROL", "SENSORS", ImportType::AllRules, "*").unwrap();
539
540 assert!(manager.is_rule_visible("sensor-temp", "CONTROL").unwrap());
542 assert!(manager.is_rule_visible("sensor-pressure", "CONTROL").unwrap());
543 }
544
545 #[test]
546 fn test_pattern_matching() {
547 assert!(pattern_matches("*", "anything"));
548 assert!(pattern_matches("sensor-*", "sensor-temp"));
549 assert!(pattern_matches("sensor-*", "sensor-pressure"));
550 assert!(!pattern_matches("sensor-*", "control-temp"));
551 assert!(pattern_matches("*-temp", "sensor-temp"));
552 assert!(pattern_matches("exact", "exact"));
553 assert!(!pattern_matches("exact", "not-exact"));
554 }
555
556 #[test]
557 fn test_main_module_default_export() {
558 let manager = ModuleManager::new();
559 let main_module = manager.get_module("MAIN").unwrap();
560
561 assert!(matches!(main_module.exports, ExportList::All));
563 }
564
565 #[test]
566 fn test_user_module_default_export() {
567 let mut manager = ModuleManager::new();
568 manager.create_module("USER").unwrap();
569 let user_module = manager.get_module("USER").unwrap();
570
571 assert!(matches!(user_module.exports, ExportList::None));
573 }
574
575 #[test]
576 fn test_visibility_own_rules() {
577 let mut manager = ModuleManager::new();
578 manager.create_module("TEST").unwrap();
579
580 let test_module = manager.get_module_mut("TEST").unwrap();
581 test_module.add_rule("my-rule");
582
583 assert!(manager.is_rule_visible("my-rule", "TEST").unwrap());
585 }
586
587 #[test]
588 fn test_get_visible_rules() {
589 let mut manager = ModuleManager::new();
590 manager.create_module("MOD1").unwrap();
591 manager.create_module("MOD2").unwrap();
592
593 let mod1 = manager.get_module_mut("MOD1").unwrap();
595 mod1.add_rule("rule1");
596 mod1.add_rule("rule2");
597 mod1.set_exports(ExportList::All);
598
599 let mod2 = manager.get_module_mut("MOD2").unwrap();
601 mod2.add_rule("rule3");
602
603 manager.import_from("MOD2", "MOD1", ImportType::AllRules, "*").unwrap();
605
606 let visible = manager.get_visible_rules("MOD2").unwrap();
607 assert!(visible.contains(&"rule1".to_string()));
608 assert!(visible.contains(&"rule2".to_string()));
609 assert!(visible.contains(&"rule3".to_string()));
610 assert_eq!(visible.len(), 3);
611 }
612
613 #[test]
614 fn test_module_stats() {
615 let mut manager = ModuleManager::new();
616 manager.create_module("TEST").unwrap();
617
618 let test_module = manager.get_module_mut("TEST").unwrap();
619 test_module.add_rule("rule1");
620 test_module.add_template("template1");
621
622 let stats = manager.get_stats();
623 assert_eq!(stats.total_modules, 2); assert_eq!(stats.current_focus, "MAIN");
625
626 let test_info = stats.modules.get("TEST").unwrap();
627 assert_eq!(test_info.rules_count, 1);
628 assert_eq!(test_info.templates_count, 1);
629 }
630}