1use std::any::TypeId;
2use std::collections::{HashMap, HashSet};
3use std::sync::Arc;
4
5use crate::container::binding::{ServiceBinder, ServiceBindings};
6use crate::container::ioc_container::IocContainer;
7use crate::errors::CoreError;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct ModuleId {
12 name: String,
13 type_id: TypeId,
14}
15
16impl ModuleId {
17 pub fn of<T: ServiceModule + 'static>() -> Self {
19 Self {
20 name: std::any::type_name::<T>().to_string(),
21 type_id: TypeId::of::<T>(),
22 }
23 }
24
25 pub fn named(name: &str) -> Self {
27 Self {
28 name: name.to_string(),
29 type_id: TypeId::of::<()>(), }
31 }
32
33 pub fn name(&self) -> &str {
34 &self.name
35 }
36}
37
38pub trait ServiceModule: Send + Sync
40where
41 Self: 'static,
42{
43 fn id(&self) -> ModuleId
45 where
46 Self: Sized,
47 {
48 ModuleId::of::<Self>()
49 }
50
51 fn name(&self) -> &str {
53 std::any::type_name::<Self>()
54 }
55
56 fn description(&self) -> Option<&str> {
58 None
59 }
60
61 fn version(&self) -> Option<&str> {
63 None
64 }
65
66 fn configure(&self, services: &mut ServiceBindings) {
68 let _ = services;
71 }
72
73 fn depends_on(&self) -> Vec<ModuleId> {
75 vec![]
76 }
77
78 fn initialize(
80 &self,
81 container: &IocContainer,
82 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), CoreError>> + Send + '_>>
83 {
84 let _ = container; Box::pin(async move { Ok(()) })
86 }
87
88 fn shutdown(
90 &self,
91 container: &IocContainer,
92 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), CoreError>> + Send + '_>>
93 {
94 let _ = container; Box::pin(async move { Ok(()) })
96 }
97
98 fn is_compatible_with(&self, other_version: &str) -> bool {
100 let _ = other_version; true
102 }
103
104 fn metadata(&self) -> ModuleMetadata
106 where
107 Self: Sized,
108 {
109 ModuleMetadata {
110 id: self.id(),
111 name: self.name().to_string(),
112 description: self.description().map(|s| s.to_string()),
113 version: self.version().map(|s| s.to_string()),
114 dependencies: self.depends_on(),
115 }
116 }
117}
118
119#[derive(Debug, Clone)]
121pub struct ModuleMetadata {
122 pub id: ModuleId,
123 pub name: String,
124 pub description: Option<String>,
125 pub version: Option<String>,
126 pub dependencies: Vec<ModuleId>,
127}
128
129#[derive(Debug, Clone)]
131pub struct ModuleConfig {
132 pub auto_initialize: bool,
134 pub init_timeout: Option<std::time::Duration>,
136 pub validate_dependencies: bool,
138 pub parameters: HashMap<String, String>,
140}
141
142impl Default for ModuleConfig {
143 fn default() -> Self {
144 Self {
145 auto_initialize: true,
146 init_timeout: Some(std::time::Duration::from_secs(30)),
147 validate_dependencies: true,
148 parameters: HashMap::new(),
149 }
150 }
151}
152
153#[derive(Debug, Clone, PartialEq)]
155pub enum ModuleState {
156 Registered,
158 Configured,
160 Initialized,
162 Failed(String),
164 Shutdown,
166}
167
168#[derive(Debug, Clone)]
170pub struct LoadedModule {
171 pub metadata: ModuleMetadata,
172 pub config: ModuleConfig,
173 pub state: ModuleState,
174 pub load_order: usize,
175 pub init_duration: Option<std::time::Duration>,
176}
177
178pub struct ModuleRegistry {
181 modules: HashMap<ModuleId, Arc<dyn ServiceModule>>,
182 loaded_modules: HashMap<ModuleId, LoadedModule>,
183 dependency_graph: HashMap<ModuleId, Vec<ModuleId>>,
184 load_order: Vec<ModuleId>,
185}
186
187impl ModuleRegistry {
188 pub fn new() -> Self {
190 Self {
191 modules: HashMap::new(),
192 loaded_modules: HashMap::new(),
193 dependency_graph: HashMap::new(),
194 load_order: Vec::new(),
195 }
196 }
197
198 pub fn register_module<T: ServiceModule + 'static>(
200 &mut self,
201 module: T,
202 config: Option<ModuleConfig>,
203 ) -> Result<(), CoreError> {
204 let module_arc = Arc::new(module);
205 let module_id = module_arc.id();
206 let metadata = module_arc.metadata();
207
208 if self.modules.contains_key(&module_id) {
210 return Err(CoreError::InvalidServiceDescriptor {
211 message: format!("Module {} is already registered", metadata.name),
212 });
213 }
214
215 self.modules.insert(module_id.clone(), module_arc);
217
218 self.dependency_graph
220 .insert(module_id.clone(), metadata.dependencies.clone());
221
222 let loaded_module = LoadedModule {
224 metadata,
225 config: config.unwrap_or_default(),
226 state: ModuleState::Registered,
227 load_order: 0, init_duration: None,
229 };
230
231 self.loaded_modules.insert(module_id, loaded_module);
232
233 Ok(())
234 }
235
236 pub fn register_modules<T: ServiceModule + 'static>(
238 &mut self,
239 modules: Vec<T>,
240 ) -> Result<(), CoreError> {
241 for module in modules {
242 self.register_module(module, None)?;
243 }
244 Ok(())
245 }
246
247 pub fn calculate_load_order(&mut self) -> Result<Vec<ModuleId>, CoreError> {
249 let mut visited = HashSet::new();
250 let mut temp_visited = HashSet::new();
251 let mut order = Vec::new();
252
253 let mut module_ids: Vec<_> = self.modules.keys().cloned().collect();
256 module_ids.sort_by(|a, b| a.name().cmp(b.name()));
257
258 for module_id in module_ids {
259 if !visited.contains(&module_id) {
260 self.visit_module_for_ordering(
261 &module_id,
262 &mut visited,
263 &mut temp_visited,
264 &mut order,
265 )?;
266 }
267 }
268
269 self.load_order = order.clone();
271
272 for (index, module_id) in order.iter().enumerate() {
274 if let Some(loaded_module) = self.loaded_modules.get_mut(module_id) {
275 loaded_module.load_order = index;
276 }
277 }
278
279 Ok(order)
280 }
281
282 fn visit_module_for_ordering(
284 &self,
285 module_id: &ModuleId,
286 visited: &mut HashSet<ModuleId>,
287 temp_visited: &mut HashSet<ModuleId>,
288 order: &mut Vec<ModuleId>,
289 ) -> Result<(), CoreError> {
290 if temp_visited.contains(module_id) {
291 return Err(CoreError::InvalidServiceDescriptor {
292 message: format!(
293 "Circular dependency detected involving module {}",
294 module_id.name()
295 ),
296 });
297 }
298
299 if visited.contains(module_id) {
300 return Ok(());
301 }
302
303 temp_visited.insert(module_id.clone());
304
305 if let Some(dependencies) = self.dependency_graph.get(module_id) {
307 for dep_id in dependencies {
308 if !self.modules.contains_key(dep_id) {
310 return Err(CoreError::ServiceNotFound {
311 service_type: format!(
312 "Module dependency {} for module {}",
313 dep_id.name(),
314 module_id.name()
315 ),
316 });
317 }
318 self.visit_module_for_ordering(dep_id, visited, temp_visited, order)?;
319 }
320 }
321
322 temp_visited.remove(module_id);
323 visited.insert(module_id.clone());
324 order.push(module_id.clone());
325
326 Ok(())
327 }
328
329 pub fn configure_all<T: ServiceBinder>(&mut self, container: &mut T) -> Result<(), CoreError> {
331 let order = if self.load_order.is_empty() {
332 self.calculate_load_order()?
333 } else {
334 self.load_order.clone()
335 };
336
337 let mut bindings = ServiceBindings::new();
339
340 for module_id in &order {
341 let module = self
342 .modules
343 .get(module_id)
344 .ok_or_else(|| CoreError::ServiceNotFound {
345 service_type: format!("Module {}", module_id.name()),
346 })?
347 .clone();
348
349 module.configure(&mut bindings);
351
352 if let Some(loaded_module) = self.loaded_modules.get_mut(module_id) {
354 loaded_module.state = ModuleState::Configured;
355 }
356 }
357
358 for descriptor in bindings.into_descriptors() {
360 container.add_service_descriptor(descriptor)?;
361 }
362
363 Ok(())
364 }
365
366 pub async fn initialize_all(&mut self, container: &IocContainer) -> Result<(), CoreError> {
368 let order = self.load_order.clone();
369
370 for module_id in order {
371 let start_time = std::time::Instant::now();
372
373 let module = self
374 .modules
375 .get(&module_id)
376 .ok_or_else(|| CoreError::ServiceNotFound {
377 service_type: format!("Module {}", module_id.name()),
378 })?
379 .clone();
380
381 let config = self
383 .loaded_modules
384 .get(&module_id)
385 .map(|m| m.config.clone())
386 .unwrap_or_default();
387
388 let result = if let Some(timeout) = config.init_timeout {
390 tokio::time::timeout(timeout, module.initialize(container))
391 .await
392 .map_err(|_| CoreError::InvalidServiceDescriptor {
393 message: format!("Module {} initialization timed out", module_id.name()),
394 })?
395 } else {
396 module.initialize(container).await
397 };
398
399 let duration = start_time.elapsed();
400
401 if let Some(loaded_module) = self.loaded_modules.get_mut(&module_id) {
403 loaded_module.init_duration = Some(duration);
404 loaded_module.state = match result {
405 Ok(()) => ModuleState::Initialized,
406 Err(ref e) => ModuleState::Failed(e.to_string()),
407 };
408 }
409
410 result?;
411 }
412
413 Ok(())
414 }
415
416 pub async fn shutdown_all(&mut self, container: &IocContainer) -> Result<(), CoreError> {
418 let mut order = self.load_order.clone();
419 order.reverse(); for module_id in order {
422 let module = self
423 .modules
424 .get(&module_id)
425 .ok_or_else(|| CoreError::ServiceNotFound {
426 service_type: format!("Module {}", module_id.name()),
427 })?
428 .clone();
429
430 if let Err(e) = module.shutdown(container).await {
431 eprintln!(
432 "Warning: Module {} shutdown failed: {}",
433 module_id.name(),
434 e
435 );
436 }
438
439 if let Some(loaded_module) = self.loaded_modules.get_mut(&module_id) {
441 loaded_module.state = ModuleState::Shutdown;
442 }
443 }
444
445 Ok(())
446 }
447
448 pub fn get_module(&self, module_id: &ModuleId) -> Option<&Arc<dyn ServiceModule>> {
450 self.modules.get(module_id)
451 }
452
453 pub fn get_loaded_module(&self, module_id: &ModuleId) -> Option<&LoadedModule> {
455 self.loaded_modules.get(module_id)
456 }
457
458 pub fn get_all_loaded_modules(&self) -> Vec<&LoadedModule> {
460 self.loaded_modules.values().collect()
461 }
462
463 pub fn is_module_ready(&self, module_id: &ModuleId) -> bool {
465 self.loaded_modules
466 .get(module_id)
467 .map(|m| m.state == ModuleState::Initialized)
468 .unwrap_or(false)
469 }
470
471 pub fn get_dependency_graph(&self) -> &HashMap<ModuleId, Vec<ModuleId>> {
473 &self.dependency_graph
474 }
475
476 pub fn get_load_order(&self) -> &[ModuleId] {
478 &self.load_order
479 }
480
481 pub fn validate_dependencies(&self) -> Result<(), Vec<CoreError>> {
483 let mut errors = Vec::new();
484
485 for (module_id, dependencies) in &self.dependency_graph {
486 for dep_id in dependencies {
487 if !self.modules.contains_key(dep_id) {
488 errors.push(CoreError::ServiceNotFound {
489 service_type: format!(
490 "Module dependency {} for module {}",
491 dep_id.name(),
492 module_id.name()
493 ),
494 });
495 }
496 }
497 }
498
499 if errors.is_empty() {
500 Ok(())
501 } else {
502 Err(errors)
503 }
504 }
505}
506
507impl Default for ModuleRegistry {
508 fn default() -> Self {
509 Self::new()
510 }
511}
512
513pub struct ModularContainerBuilder<T>
515where
516 T: ServiceBinder,
517{
518 registry: ModuleRegistry,
519 container: T,
520}
521
522impl<T> ModularContainerBuilder<T>
523where
524 T: ServiceBinder,
525{
526 pub fn new(container: T) -> Self {
528 Self {
529 registry: ModuleRegistry::new(),
530 container,
531 }
532 }
533
534 pub fn add_module<M: ServiceModule + 'static>(mut self, module: M) -> Result<Self, CoreError> {
536 self.registry.register_module(module, None)?;
537 Ok(self)
538 }
539
540 pub fn add_module_with_config<M: ServiceModule + 'static>(
542 mut self,
543 module: M,
544 config: ModuleConfig,
545 ) -> Result<Self, CoreError> {
546 self.registry.register_module(module, Some(config))?;
547 Ok(self)
548 }
549
550 pub fn add_modules<M: ServiceModule + 'static>(
552 mut self,
553 modules: Vec<M>,
554 ) -> Result<Self, CoreError> {
555 self.registry.register_modules(modules)?;
556 Ok(self)
557 }
558
559 pub fn build(mut self) -> Result<(T, ModuleRegistry), CoreError> {
561 self.registry
563 .validate_dependencies()
564 .map_err(|errors| errors.into_iter().next().unwrap())?; self.registry.configure_all(&mut self.container)?;
568
569 Ok((self.container, self.registry))
570 }
571}
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576 use crate::container::ioc_container::IocContainer;
577
578 struct CoreModule;
580
581 impl ServiceModule for CoreModule {
582 fn name(&self) -> &str {
583 "Core Module"
584 }
585
586 fn description(&self) -> Option<&str> {
587 Some("Core application services")
588 }
589
590 fn configure(&self, _services: &mut ServiceBindings) {
591 }
593 }
594
595 struct AuthModule;
596
597 impl ServiceModule for AuthModule {
598 fn name(&self) -> &str {
599 "Auth Module"
600 }
601
602 fn depends_on(&self) -> Vec<ModuleId> {
603 vec![ModuleId::of::<CoreModule>()]
604 }
605
606 fn configure(&self, _services: &mut ServiceBindings) {
607 }
609 }
610
611 struct ApiModule;
612
613 impl ServiceModule for ApiModule {
614 fn name(&self) -> &str {
615 "API Module"
616 }
617
618 fn depends_on(&self) -> Vec<ModuleId> {
619 vec![ModuleId::of::<AuthModule>(), ModuleId::of::<CoreModule>()]
620 }
621
622 fn configure(&self, _services: &mut ServiceBindings) {
623 }
625 }
626
627 #[test]
628 fn test_module_registration() {
629 let mut registry = ModuleRegistry::new();
630
631 registry.register_module(CoreModule, None).unwrap();
632 registry.register_module(AuthModule, None).unwrap();
633
634 assert_eq!(registry.modules.len(), 2);
635 assert_eq!(registry.loaded_modules.len(), 2);
636 }
637
638 #[test]
639 fn test_dependency_ordering() {
640 let mut registry = ModuleRegistry::new();
641
642 registry.register_module(ApiModule, None).unwrap();
643 registry.register_module(CoreModule, None).unwrap();
644 registry.register_module(AuthModule, None).unwrap();
645
646 let order = registry.calculate_load_order().unwrap();
647 let order_names: Vec<String> = order.iter().map(|id| id.name().to_string()).collect();
648
649 assert_eq!(order_names[0], std::any::type_name::<CoreModule>());
653 assert_eq!(order_names[1], std::any::type_name::<AuthModule>());
654 assert_eq!(order_names[2], std::any::type_name::<ApiModule>());
655 }
656
657 #[test]
658 fn test_circular_dependency_detection() {
659 struct Module1;
660 struct Module2;
661
662 impl ServiceModule for Module1 {
663 fn depends_on(&self) -> Vec<ModuleId> {
664 vec![ModuleId::of::<Module2>()]
665 }
666
667 fn configure(&self, _services: &mut ServiceBindings) {}
668 }
669
670 impl ServiceModule for Module2 {
671 fn depends_on(&self) -> Vec<ModuleId> {
672 vec![ModuleId::of::<Module1>()] }
674
675 fn configure(&self, _services: &mut ServiceBindings) {}
676 }
677
678 let mut registry = ModuleRegistry::new();
679 registry.register_module(Module1, None).unwrap();
680 registry.register_module(Module2, None).unwrap();
681
682 let result = registry.calculate_load_order();
683 assert!(result.is_err());
684 }
685
686 #[test]
687 fn test_missing_dependency_validation() {
688 struct ModuleWithMissingDep;
689
690 impl ServiceModule for ModuleWithMissingDep {
691 fn depends_on(&self) -> Vec<ModuleId> {
692 vec![ModuleId::named("NonExistentModule")]
693 }
694
695 fn configure(&self, _services: &mut ServiceBindings) {}
696 }
697
698 let mut registry = ModuleRegistry::new();
699 registry
700 .register_module(ModuleWithMissingDep, None)
701 .unwrap();
702
703 let result = registry.calculate_load_order();
704 assert!(result.is_err());
705 }
706
707 #[tokio::test]
708 async fn test_modular_container_builder() {
709 let container = IocContainer::new();
710 let builder = ModularContainerBuilder::new(container);
711
712 let result = builder
713 .add_module(CoreModule)
714 .unwrap()
715 .add_module(AuthModule)
716 .unwrap()
717 .add_module(ApiModule)
718 .unwrap()
719 .build();
720
721 assert!(result.is_ok());
722 let (_container, registry) = result.unwrap();
723
724 assert_eq!(registry.get_load_order().len(), 3);
726 }
727
728 #[test]
729 fn test_module_service_configuration() {
730 use crate::container::ioc_builder::IocContainerBuilder;
731
732 struct TestModule;
734
735 #[derive(Default)]
736 struct TestService {
737 #[allow(dead_code)]
738 pub name: String,
739 }
740
741 impl ServiceModule for TestModule {
742 fn configure(&self, services: &mut ServiceBindings) {
743 services.bind::<TestService, TestService>();
744 }
745 }
746
747 let mut registry = ModuleRegistry::new();
748 registry.register_module(TestModule, None).unwrap();
749
750 let mut container_builder = IocContainerBuilder::new();
751
752 registry.configure_all(&mut container_builder).unwrap();
754
755 let container = container_builder.build().unwrap();
757
758 let service = container.resolve::<TestService>();
760 assert!(service.is_ok());
761 }
762}