elif_core/modules/
metadata.rs1use std::collections::HashMap;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct CompileTimeModuleMetadata {
11 pub name: String,
13 pub controllers: Vec<String>,
15 pub providers: Vec<String>,
17 pub imports: Vec<String>,
19 pub exports: Vec<String>,
21}
22
23impl CompileTimeModuleMetadata {
24 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 pub fn with_controller(mut self, controller: String) -> Self {
37 self.controllers.push(controller);
38 self
39 }
40
41 pub fn with_controllers(mut self, controllers: Vec<String>) -> Self {
43 self.controllers.extend(controllers);
44 self
45 }
46
47 pub fn with_provider(mut self, provider: String) -> Self {
49 self.providers.push(provider);
50 self
51 }
52
53 pub fn with_providers(mut self, providers: Vec<String>) -> Self {
55 self.providers.extend(providers);
56 self
57 }
58
59 pub fn with_import(mut self, import: String) -> Self {
61 self.imports.push(import);
62 self
63 }
64
65 pub fn with_imports(mut self, imports: Vec<String>) -> Self {
67 self.imports.extend(imports);
68 self
69 }
70
71 pub fn with_export(mut self, export: String) -> Self {
73 self.exports.push(export);
74 self
75 }
76
77 pub fn with_exports(mut self, exports: Vec<String>) -> Self {
79 self.exports.extend(exports);
80 self
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct CompileTimeModuleRegistry {
87 modules: HashMap<String, CompileTimeModuleMetadata>,
89 dependency_graph: HashMap<String, Vec<String>>,
91}
92
93impl CompileTimeModuleRegistry {
94 pub fn new() -> Self {
96 Self {
97 modules: HashMap::new(),
98 dependency_graph: HashMap::new(),
99 }
100 }
101
102 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 pub fn all_modules(&self) -> Vec<&CompileTimeModuleMetadata> {
113 self.modules.values().collect()
114 }
115
116 pub fn find_module(&self, name: &str) -> Option<&CompileTimeModuleMetadata> {
118 self.modules.get(name)
119 }
120
121 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 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 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 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 pub fn dependency_graph(&self) -> &HashMap<String, Vec<String>> {
155 &self.dependency_graph
156 }
157
158 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 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 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 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 pub fn module_count(&self) -> usize {
225 self.modules.len()
226 }
227
228 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
240use std::sync::{Mutex, OnceLock};
242
243static GLOBAL_MODULE_REGISTRY: OnceLock<Mutex<CompileTimeModuleRegistry>> = OnceLock::new();
244
245pub 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
258pub 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 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()); 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 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 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()); 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 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}