elif_core/modules/
registry.rs

1use crate::container::ContainerBuilder;
2use crate::modules::{MiddlewareDefinition, Module, ModuleError, ModuleMetadata, RouteDefinition};
3use std::collections::HashMap;
4
5/// Module registry for managing module lifecycle and dependencies
6pub struct ModuleRegistry {
7    modules: Vec<Box<dyn Module>>,
8    loading_order: Vec<usize>,
9    routes: Vec<RouteDefinition>,
10    middleware: Vec<MiddlewareDefinition>,
11    metadata_cache: HashMap<String, ModuleMetadata>,
12}
13
14impl ModuleRegistry {
15    /// Create a new module registry
16    pub fn new() -> Self {
17        Self {
18            modules: Vec::new(),
19            loading_order: Vec::new(),
20            routes: Vec::new(),
21            middleware: Vec::new(),
22            metadata_cache: HashMap::new(),
23        }
24    }
25
26    /// Register a module
27    pub fn register<M: Module + 'static>(&mut self, module: M) {
28        let metadata = ModuleMetadata::from_module(&module);
29        let name = metadata.name.clone();
30
31        self.modules.push(Box::new(module));
32        self.metadata_cache.insert(name, metadata);
33    }
34
35    /// Get the number of registered modules
36    pub fn module_count(&self) -> usize {
37        self.modules.len()
38    }
39
40    /// Get module metadata by name
41    pub fn get_metadata(&self, name: &str) -> Option<&ModuleMetadata> {
42        self.metadata_cache.get(name)
43    }
44
45    /// Get all module metadata
46    pub fn all_metadata(&self) -> Vec<&ModuleMetadata> {
47        self.metadata_cache.values().collect()
48    }
49
50    /// Check if a module is registered
51    pub fn has_module(&self, name: &str) -> bool {
52        self.modules.iter().any(|m| m.name() == name)
53    }
54
55    /// Resolve module dependencies and determine loading order
56    pub fn resolve_dependencies(&mut self) -> Result<(), ModuleError> {
57        let module_count = self.modules.len();
58
59        // Create name to index mapping
60        let name_to_index: HashMap<String, usize> = self
61            .modules
62            .iter()
63            .enumerate()
64            .map(|(i, m)| (m.name().to_string(), i))
65            .collect();
66
67        // Perform topological sort
68        let mut visited = vec![false; module_count];
69        let mut temp_mark = vec![false; module_count];
70        let mut result = Vec::new();
71
72        for i in 0..module_count {
73            if !visited[i] {
74                self.visit_module(i, &name_to_index, &mut visited, &mut temp_mark, &mut result)?;
75            }
76        }
77
78        self.loading_order = result;
79        Ok(())
80    }
81
82    /// Visit module for dependency resolution (topological sort)
83    fn visit_module(
84        &self,
85        index: usize,
86        name_to_index: &HashMap<String, usize>,
87        visited: &mut Vec<bool>,
88        temp_mark: &mut Vec<bool>,
89        result: &mut Vec<usize>,
90    ) -> Result<(), ModuleError> {
91        if temp_mark[index] {
92            return Err(ModuleError::CircularDependency {
93                module: self.modules[index].name().to_string(),
94            });
95        }
96
97        if visited[index] {
98            return Ok(());
99        }
100
101        temp_mark[index] = true;
102
103        // Visit all dependencies first
104        let dependencies = self.modules[index].dependencies();
105        for dep_name in dependencies {
106            if let Some(&dep_index) = name_to_index.get(dep_name) {
107                self.visit_module(dep_index, name_to_index, visited, temp_mark, result)?;
108            } else {
109                return Err(ModuleError::MissingDependency {
110                    module: self.modules[index].name().to_string(),
111                    dependency: dep_name.to_string(),
112                });
113            }
114        }
115
116        temp_mark[index] = false;
117        visited[index] = true;
118        result.push(index);
119
120        Ok(())
121    }
122
123    /// Configure all modules with the container builder
124    pub fn configure_all(
125        &self,
126        mut builder: ContainerBuilder,
127    ) -> Result<ContainerBuilder, ModuleError> {
128        for &index in &self.loading_order {
129            let module = &self.modules[index];
130            tracing::info!("Configuring module: {}", module.name());
131            builder = module.configure(builder)?;
132        }
133        Ok(builder)
134    }
135
136    /// Boot all modules after container is built
137    pub fn boot_all(&self, container: &crate::container::Container) -> Result<(), ModuleError> {
138        for &index in &self.loading_order {
139            let module = &self.modules[index];
140            tracing::info!("Booting module: {}", module.name());
141            module
142                .boot(container)
143                .map_err(|e| ModuleError::BootFailed {
144                    message: format!("Failed to boot module '{}': {}", module.name(), e),
145                })?;
146        }
147        Ok(())
148    }
149
150    /// Collect all routes from registered modules
151    pub fn collect_routes(&mut self) -> Vec<RouteDefinition> {
152        if self.routes.is_empty() {
153            for module in &self.modules {
154                self.routes.extend(module.routes());
155            }
156        }
157        self.routes.clone()
158    }
159
160    /// Collect all middleware from registered modules
161    pub fn collect_middleware(&mut self) -> Vec<MiddlewareDefinition> {
162        if self.middleware.is_empty() {
163            for module in &self.modules {
164                self.middleware.extend(module.middleware());
165            }
166            // Sort middleware by priority
167            self.middleware.sort();
168        }
169        self.middleware.clone()
170    }
171
172    /// Get modules in loading order
173    pub fn modules_in_order(&self) -> Vec<&dyn Module> {
174        self.loading_order
175            .iter()
176            .map(|&index| self.modules[index].as_ref())
177            .collect()
178    }
179
180    /// Validate all modules
181    pub fn validate(&self) -> Result<(), ModuleError> {
182        // Check for duplicate module names
183        let mut names = std::collections::HashSet::new();
184        for module in &self.modules {
185            let name = module.name();
186            if names.contains(name) {
187                return Err(ModuleError::ConfigurationFailed {
188                    message: format!("Duplicate module name: {}", name),
189                });
190            }
191            names.insert(name);
192        }
193
194        // Check dependencies exist
195        for module in &self.modules {
196            for dep in module.dependencies() {
197                if !names.contains(dep) {
198                    return Err(ModuleError::MissingDependency {
199                        module: module.name().to_string(),
200                        dependency: dep.to_string(),
201                    });
202                }
203            }
204        }
205
206        Ok(())
207    }
208
209    /// Clear all modules (useful for testing)
210    pub fn clear(&mut self) {
211        self.modules.clear();
212        self.loading_order.clear();
213        self.routes.clear();
214        self.middleware.clear();
215        self.metadata_cache.clear();
216    }
217}
218
219impl Default for ModuleRegistry {
220    fn default() -> Self {
221        Self::new()
222    }
223}
224
225impl std::fmt::Debug for ModuleRegistry {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        f.debug_struct("ModuleRegistry")
228            .field("module_count", &self.modules.len())
229            .field("route_count", &self.routes.len())
230            .field("middleware_count", &self.middleware.len())
231            .field("loading_order", &self.loading_order)
232            .finish()
233    }
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239    use crate::modules::BaseModule;
240
241    #[tokio::test]
242    async fn test_module_registry() -> Result<(), ModuleError> {
243        let mut registry = ModuleRegistry::new();
244
245        // Register modules with dependencies
246        let module_a = BaseModule::new("module_a");
247        let module_b = BaseModule::new("module_b").with_dependencies(vec!["module_a"]);
248        let module_c = BaseModule::new("module_c").with_dependencies(vec!["module_b", "module_a"]);
249
250        registry.register(module_a);
251        registry.register(module_b);
252        registry.register(module_c);
253
254        assert_eq!(registry.module_count(), 3);
255
256        // Resolve dependencies
257        registry.resolve_dependencies()?;
258
259        // Check loading order
260        let modules_in_order = registry.modules_in_order();
261        let names: Vec<&str> = modules_in_order.iter().map(|m| m.name()).collect();
262
263        // module_a should be first, module_c should be last
264        assert_eq!(names[0], "module_a");
265        assert_eq!(names[2], "module_c");
266
267        Ok(())
268    }
269
270    #[tokio::test]
271    async fn test_circular_dependency_detection() {
272        let mut registry = ModuleRegistry::new();
273
274        let module_a = BaseModule::new("module_a").with_dependencies(vec!["module_b"]);
275        let module_b = BaseModule::new("module_b").with_dependencies(vec!["module_a"]);
276
277        registry.register(module_a);
278        registry.register(module_b);
279
280        let result = registry.resolve_dependencies();
281        assert!(result.is_err());
282
283        if let Err(ModuleError::CircularDependency { module }) = result {
284            assert!(module == "module_a" || module == "module_b");
285        } else {
286            panic!("Expected circular dependency error");
287        }
288    }
289}