elif_core/modules/
registry.rs1use crate::container::ContainerBuilder;
2use crate::modules::{Module, ModuleError, ModuleMetadata, RouteDefinition, MiddlewareDefinition};
3use std::collections::HashMap;
4
5pub 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 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 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 pub fn module_count(&self) -> usize {
37 self.modules.len()
38 }
39
40 pub fn get_metadata(&self, name: &str) -> Option<&ModuleMetadata> {
42 self.metadata_cache.get(name)
43 }
44
45 pub fn all_metadata(&self) -> Vec<&ModuleMetadata> {
47 self.metadata_cache.values().collect()
48 }
49
50 pub fn has_module(&self, name: &str) -> bool {
52 self.modules.iter().any(|m| m.name() == name)
53 }
54
55 pub fn resolve_dependencies(&mut self) -> Result<(), ModuleError> {
57 let module_count = self.modules.len();
58
59 let name_to_index: HashMap<String, usize> = self.modules
61 .iter()
62 .enumerate()
63 .map(|(i, m)| (m.name().to_string(), i))
64 .collect();
65
66 let mut visited = vec![false; module_count];
68 let mut temp_mark = vec![false; module_count];
69 let mut result = Vec::new();
70
71 for i in 0..module_count {
72 if !visited[i] {
73 self.visit_module(i, &name_to_index, &mut visited, &mut temp_mark, &mut result)?;
74 }
75 }
76
77 self.loading_order = result;
78 Ok(())
79 }
80
81 fn visit_module(
83 &self,
84 index: usize,
85 name_to_index: &HashMap<String, usize>,
86 visited: &mut Vec<bool>,
87 temp_mark: &mut Vec<bool>,
88 result: &mut Vec<usize>,
89 ) -> Result<(), ModuleError> {
90 if temp_mark[index] {
91 return Err(ModuleError::CircularDependency {
92 module: self.modules[index].name().to_string(),
93 });
94 }
95
96 if visited[index] {
97 return Ok(());
98 }
99
100 temp_mark[index] = true;
101
102 let dependencies = self.modules[index].dependencies();
104 for dep_name in dependencies {
105 if let Some(&dep_index) = name_to_index.get(dep_name) {
106 self.visit_module(dep_index, name_to_index, visited, temp_mark, result)?;
107 } else {
108 return Err(ModuleError::MissingDependency {
109 module: self.modules[index].name().to_string(),
110 dependency: dep_name.to_string(),
111 });
112 }
113 }
114
115 temp_mark[index] = false;
116 visited[index] = true;
117 result.push(index);
118
119 Ok(())
120 }
121
122 pub fn configure_all(&self, mut builder: ContainerBuilder) -> Result<ContainerBuilder, ModuleError> {
124 for &index in &self.loading_order {
125 let module = &self.modules[index];
126 tracing::info!("Configuring module: {}", module.name());
127 builder = module.configure(builder)?;
128 }
129 Ok(builder)
130 }
131
132 pub fn boot_all(&self, container: &crate::container::Container) -> Result<(), ModuleError> {
134 for &index in &self.loading_order {
135 let module = &self.modules[index];
136 tracing::info!("Booting module: {}", module.name());
137 module.boot(container).map_err(|e| {
138 ModuleError::BootFailed {
139 message: format!("Failed to boot module '{}': {}", module.name(), e),
140 }
141 })?;
142 }
143 Ok(())
144 }
145
146 pub fn collect_routes(&mut self) -> Vec<RouteDefinition> {
148 if self.routes.is_empty() {
149 for module in &self.modules {
150 self.routes.extend(module.routes());
151 }
152 }
153 self.routes.clone()
154 }
155
156 pub fn collect_middleware(&mut self) -> Vec<MiddlewareDefinition> {
158 if self.middleware.is_empty() {
159 for module in &self.modules {
160 self.middleware.extend(module.middleware());
161 }
162 self.middleware.sort();
164 }
165 self.middleware.clone()
166 }
167
168 pub fn modules_in_order(&self) -> Vec<&dyn Module> {
170 self.loading_order
171 .iter()
172 .map(|&index| self.modules[index].as_ref())
173 .collect()
174 }
175
176 pub fn validate(&self) -> Result<(), ModuleError> {
178 let mut names = std::collections::HashSet::new();
180 for module in &self.modules {
181 let name = module.name();
182 if names.contains(name) {
183 return Err(ModuleError::ConfigurationFailed {
184 message: format!("Duplicate module name: {}", name),
185 });
186 }
187 names.insert(name);
188 }
189
190 for module in &self.modules {
192 for dep in module.dependencies() {
193 if !names.contains(dep) {
194 return Err(ModuleError::MissingDependency {
195 module: module.name().to_string(),
196 dependency: dep.to_string(),
197 });
198 }
199 }
200 }
201
202 Ok(())
203 }
204
205 pub fn clear(&mut self) {
207 self.modules.clear();
208 self.loading_order.clear();
209 self.routes.clear();
210 self.middleware.clear();
211 self.metadata_cache.clear();
212 }
213}
214
215impl Default for ModuleRegistry {
216 fn default() -> Self {
217 Self::new()
218 }
219}
220
221impl std::fmt::Debug for ModuleRegistry {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 f.debug_struct("ModuleRegistry")
224 .field("module_count", &self.modules.len())
225 .field("route_count", &self.routes.len())
226 .field("middleware_count", &self.middleware.len())
227 .field("loading_order", &self.loading_order)
228 .finish()
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::modules::{BaseModule, Module};
236
237 #[tokio::test]
238 async fn test_module_registry() -> Result<(), ModuleError> {
239 let mut registry = ModuleRegistry::new();
240
241 let module_a = BaseModule::new("module_a");
243 let module_b = BaseModule::new("module_b").with_dependencies(vec!["module_a"]);
244 let module_c = BaseModule::new("module_c").with_dependencies(vec!["module_b", "module_a"]);
245
246 registry.register(module_a);
247 registry.register(module_b);
248 registry.register(module_c);
249
250 assert_eq!(registry.module_count(), 3);
251
252 registry.resolve_dependencies()?;
254
255 let modules_in_order = registry.modules_in_order();
257 let names: Vec<&str> = modules_in_order.iter().map(|m| m.name()).collect();
258
259 assert_eq!(names[0], "module_a");
261 assert_eq!(names[2], "module_c");
262
263 Ok(())
264 }
265
266 #[tokio::test]
267 async fn test_circular_dependency_detection() {
268 let mut registry = ModuleRegistry::new();
269
270 let module_a = BaseModule::new("module_a").with_dependencies(vec!["module_b"]);
271 let module_b = BaseModule::new("module_b").with_dependencies(vec!["module_a"]);
272
273 registry.register(module_a);
274 registry.register(module_b);
275
276 let result = registry.resolve_dependencies();
277 assert!(result.is_err());
278
279 if let Err(ModuleError::CircularDependency { module }) = result {
280 assert!(module == "module_a" || module == "module_b");
281 } else {
282 panic!("Expected circular dependency error");
283 }
284 }
285}