elif_core/modules/
metadata.rs

1//! Module metadata structures for compile-time module discovery
2//!
3//! This module provides the data structures needed to support compile-time
4//! module scanning and automatic discovery of #[module] decorated types.
5
6use std::collections::HashMap;
7
8/// Metadata about a single module discovered at compile time
9#[derive(Debug, Clone, PartialEq)]
10pub struct CompileTimeModuleMetadata {
11    /// Name of the module type
12    pub name: String,
13    /// List of controller types in this module
14    pub controllers: Vec<String>,
15    /// List of provider types in this module  
16    pub providers: Vec<String>,
17    /// List of imported modules
18    pub imports: Vec<String>,
19    /// List of exported providers
20    pub exports: Vec<String>,
21}
22
23impl CompileTimeModuleMetadata {
24    /// Create a new module metadata instance
25    pub fn new(name: String) -> Self {
26        Self {
27            name,
28            controllers: Vec::new(),
29            providers: Vec::new(),
30            imports: Vec::new(),
31            exports: Vec::new(),
32        }
33    }
34
35    /// Add a controller to this module
36    pub fn with_controller(mut self, controller: String) -> Self {
37        self.controllers.push(controller);
38        self
39    }
40
41    /// Add multiple controllers to this module
42    pub fn with_controllers(mut self, controllers: Vec<String>) -> Self {
43        self.controllers.extend(controllers);
44        self
45    }
46
47    /// Add a provider to this module
48    pub fn with_provider(mut self, provider: String) -> Self {
49        self.providers.push(provider);
50        self
51    }
52
53    /// Add multiple providers to this module
54    pub fn with_providers(mut self, providers: Vec<String>) -> Self {
55        self.providers.extend(providers);
56        self
57    }
58
59    /// Add an import to this module
60    pub fn with_import(mut self, import: String) -> Self {
61        self.imports.push(import);
62        self
63    }
64
65    /// Add multiple imports to this module
66    pub fn with_imports(mut self, imports: Vec<String>) -> Self {
67        self.imports.extend(imports);
68        self
69    }
70
71    /// Add an export to this module
72    pub fn with_export(mut self, export: String) -> Self {
73        self.exports.push(export);
74        self
75    }
76
77    /// Add multiple exports to this module
78    pub fn with_exports(mut self, exports: Vec<String>) -> Self {
79        self.exports.extend(exports);
80        self
81    }
82}
83
84/// Global registry of module metadata discovered at compile time
85#[derive(Debug, Clone)]
86pub struct CompileTimeModuleRegistry {
87    /// Map of module name to metadata
88    modules: HashMap<String, CompileTimeModuleMetadata>,
89    /// Dependency graph: module name -> list of dependency names
90    dependency_graph: HashMap<String, Vec<String>>,
91}
92
93impl CompileTimeModuleRegistry {
94    /// Create a new empty module registry
95    pub fn new() -> Self {
96        Self {
97            modules: HashMap::new(),
98            dependency_graph: HashMap::new(),
99        }
100    }
101
102    /// Register a module in the registry
103    pub fn register_module(&mut self, metadata: CompileTimeModuleMetadata) {
104        let module_name = metadata.name.clone();
105        let dependencies = metadata.imports.clone();
106        
107        self.modules.insert(module_name.clone(), metadata);
108        self.dependency_graph.insert(module_name, dependencies);
109    }
110
111    /// Get all registered modules
112    pub fn all_modules(&self) -> Vec<&CompileTimeModuleMetadata> {
113        self.modules.values().collect()
114    }
115
116    /// Find a module by name
117    pub fn find_module(&self, name: &str) -> Option<&CompileTimeModuleMetadata> {
118        self.modules.get(name)
119    }
120
121    /// Get all controllers from all modules
122    pub fn all_controllers(&self) -> Vec<String> {
123        self.modules
124            .values()
125            .flat_map(|module| module.controllers.iter().cloned())
126            .collect()
127    }
128
129    /// Get all providers from all modules
130    pub fn all_providers(&self) -> Vec<String> {
131        self.modules
132            .values()
133            .flat_map(|module| module.providers.iter().cloned())
134            .collect()
135    }
136
137    /// Get modules that have controllers
138    pub fn modules_with_controllers(&self) -> Vec<&CompileTimeModuleMetadata> {
139        self.modules
140            .values()
141            .filter(|module| !module.controllers.is_empty())
142            .collect()
143    }
144
145    /// Get modules that have providers
146    pub fn modules_with_providers(&self) -> Vec<&CompileTimeModuleMetadata> {
147        self.modules
148            .values()
149            .filter(|module| !module.providers.is_empty())
150            .collect()
151    }
152
153    /// Get the dependency graph
154    pub fn dependency_graph(&self) -> &HashMap<String, Vec<String>> {
155        &self.dependency_graph
156    }
157
158    /// Resolve module dependencies and return modules in dependency order
159    pub fn resolve_dependency_order(&self) -> Result<Vec<&CompileTimeModuleMetadata>, String> {
160        let mut visited = std::collections::HashSet::new();
161        let mut temp_visited = std::collections::HashSet::new();
162        let mut result = Vec::new();
163
164        // Sort module names for deterministic ordering
165        let mut module_names: Vec<_> = self.modules.keys().collect();
166        module_names.sort();
167
168        for module_name in module_names {
169            if !visited.contains(module_name) {
170                self.visit_for_topological_sort(
171                    module_name,
172                    &mut visited,
173                    &mut temp_visited,
174                    &mut result,
175                )?;
176            }
177        }
178
179        Ok(result)
180    }
181
182    /// Visit a module during topological sorting
183    fn visit_for_topological_sort<'a>(
184        &'a self,
185        module_name: &str,
186        visited: &mut std::collections::HashSet<String>,
187        temp_visited: &mut std::collections::HashSet<String>,
188        result: &mut Vec<&'a CompileTimeModuleMetadata>,
189    ) -> Result<(), String> {
190        if temp_visited.contains(module_name) {
191            return Err(format!("Circular dependency detected involving module '{}'", module_name));
192        }
193
194        if visited.contains(module_name) {
195            return Ok(());
196        }
197
198        temp_visited.insert(module_name.to_string());
199
200        // Visit dependencies first
201        if let Some(dependencies) = self.dependency_graph.get(module_name) {
202            for dep_name in dependencies {
203                if !self.modules.contains_key(dep_name) {
204                    return Err(format!(
205                        "Module '{}' depends on '{}' which is not registered",
206                        module_name, dep_name
207                    ));
208                }
209                self.visit_for_topological_sort(dep_name, visited, temp_visited, result)?;
210            }
211        }
212
213        temp_visited.remove(module_name);
214        visited.insert(module_name.to_string());
215
216        if let Some(metadata) = self.modules.get(module_name) {
217            result.push(metadata);
218        }
219
220        Ok(())
221    }
222
223    /// Get the number of registered modules
224    pub fn module_count(&self) -> usize {
225        self.modules.len()
226    }
227
228    /// Check if the registry is empty
229    pub fn is_empty(&self) -> bool {
230        self.modules.is_empty()
231    }
232}
233
234impl Default for CompileTimeModuleRegistry {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240/// Thread-safe global module registry
241use std::sync::{Mutex, OnceLock};
242
243static GLOBAL_MODULE_REGISTRY: OnceLock<Mutex<CompileTimeModuleRegistry>> = OnceLock::new();
244
245/// Register a module in the global registry
246/// 
247/// # Panics
248/// Panics if the global registry lock is poisoned, which indicates a serious
249/// inconsistency in the module system that should not be silently ignored.
250pub fn register_module_globally(metadata: CompileTimeModuleMetadata) {
251    let registry_mutex = GLOBAL_MODULE_REGISTRY.get_or_init(|| Mutex::new(CompileTimeModuleRegistry::new()));
252    registry_mutex
253        .lock()
254        .expect("Global module registry is poisoned")
255        .register_module(metadata);
256}
257
258/// Get a copy of the global module registry
259/// 
260/// # Panics
261/// Panics if the global registry lock is poisoned, which indicates a serious
262/// inconsistency in the module system that should not be silently ignored.
263pub fn get_global_module_registry() -> CompileTimeModuleRegistry {
264    let registry_mutex = GLOBAL_MODULE_REGISTRY.get_or_init(|| Mutex::new(CompileTimeModuleRegistry::new()));
265    registry_mutex
266        .lock()
267        .expect("Global module registry is poisoned")
268        .clone()
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274
275    #[test]
276    fn test_module_metadata_creation() {
277        let metadata = CompileTimeModuleMetadata::new("UserModule".to_string())
278            .with_controller("UserController".to_string())
279            .with_provider("UserService".to_string())
280            .with_import("AuthModule".to_string())
281            .with_export("UserService".to_string());
282
283        assert_eq!(metadata.name, "UserModule");
284        assert_eq!(metadata.controllers, vec!["UserController"]);
285        assert_eq!(metadata.providers, vec!["UserService"]);
286        assert_eq!(metadata.imports, vec!["AuthModule"]);
287        assert_eq!(metadata.exports, vec!["UserService"]);
288    }
289
290    #[test]
291    fn test_module_registry_basic_operations() {
292        let mut registry = CompileTimeModuleRegistry::new();
293
294        let user_module = CompileTimeModuleMetadata::new("UserModule".to_string())
295            .with_controller("UserController".to_string())
296            .with_provider("UserService".to_string());
297
298        let auth_module = CompileTimeModuleMetadata::new("AuthModule".to_string())
299            .with_provider("AuthService".to_string());
300
301        registry.register_module(user_module);
302        registry.register_module(auth_module);
303
304        assert_eq!(registry.module_count(), 2);
305        assert!(registry.find_module("UserModule").is_some());
306        assert!(registry.find_module("AuthModule").is_some());
307        assert!(registry.find_module("NonExistentModule").is_none());
308
309        let controllers = registry.all_controllers();
310        assert_eq!(controllers.len(), 1);
311        assert!(controllers.contains(&"UserController".to_string()));
312
313        let providers = registry.all_providers();
314        assert_eq!(providers.len(), 2);
315        assert!(providers.contains(&"UserService".to_string()));
316        assert!(providers.contains(&"AuthService".to_string()));
317    }
318
319    #[test]
320    fn test_dependency_resolution() {
321        let mut registry = CompileTimeModuleRegistry::new();
322
323        // Create modules with dependencies: UserModule depends on AuthModule
324        let auth_module = CompileTimeModuleMetadata::new("AuthModule".to_string())
325            .with_provider("AuthService".to_string());
326
327        let user_module = CompileTimeModuleMetadata::new("UserModule".to_string())
328            .with_controller("UserController".to_string())
329            .with_import("AuthModule".to_string()); // UserModule depends on AuthModule
330
331        registry.register_module(user_module);
332        registry.register_module(auth_module);
333
334        let resolved_order = registry.resolve_dependency_order().unwrap();
335        let module_names: Vec<_> = resolved_order.iter().map(|m| &m.name).collect();
336
337        // AuthModule should come before UserModule
338        assert_eq!(module_names.len(), 2);
339        let auth_index = module_names.iter().position(|&name| name == "AuthModule").unwrap();
340        let user_index = module_names.iter().position(|&name| name == "UserModule").unwrap();
341        assert!(auth_index < user_index);
342    }
343
344    #[test]
345    fn test_circular_dependency_detection() {
346        let mut registry = CompileTimeModuleRegistry::new();
347
348        // Create circular dependency: A depends on B, B depends on A
349        let module_a = CompileTimeModuleMetadata::new("ModuleA".to_string())
350            .with_import("ModuleB".to_string());
351
352        let module_b = CompileTimeModuleMetadata::new("ModuleB".to_string())
353            .with_import("ModuleA".to_string());
354
355        registry.register_module(module_a);
356        registry.register_module(module_b);
357
358        let result = registry.resolve_dependency_order();
359        assert!(result.is_err());
360        assert!(result.unwrap_err().contains("Circular dependency"));
361    }
362
363    #[test]
364    fn test_missing_dependency_detection() {
365        let mut registry = CompileTimeModuleRegistry::new();
366
367        let user_module = CompileTimeModuleMetadata::new("UserModule".to_string())
368            .with_import("NonExistentModule".to_string()); // Missing dependency
369
370        registry.register_module(user_module);
371
372        let result = registry.resolve_dependency_order();
373        assert!(result.is_err());
374        assert!(result.unwrap_err().contains("not registered"));
375    }
376
377    #[test]
378    fn test_global_registry() {
379        // Test global registry functions
380        let metadata = CompileTimeModuleMetadata::new("TestModule".to_string())
381            .with_controller("TestController".to_string());
382
383        register_module_globally(metadata);
384
385        let global_registry = get_global_module_registry();
386        assert!(global_registry.find_module("TestModule").is_some());
387    }
388}