elif_core/container/
module.rs

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/// Unique identifier for a service module
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct ModuleId {
12    name: String,
13    type_id: TypeId,
14}
15
16impl ModuleId {
17    /// Create a module ID for a specific type
18    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    /// Create a named module ID
26    pub fn named(name: &str) -> Self {
27        Self {
28            name: name.to_string(),
29            type_id: TypeId::of::<()>(), // Use unit type for named modules
30        }
31    }
32
33    pub fn name(&self) -> &str {
34        &self.name
35    }
36}
37
38/// Service module trait for organizing related services
39pub trait ServiceModule: Send + Sync
40where
41    Self: 'static,
42{
43    /// Get the unique identifier for this module
44    fn id(&self) -> ModuleId
45    where
46        Self: Sized,
47    {
48        ModuleId::of::<Self>()
49    }
50
51    /// Get module name (defaults to type name)
52    fn name(&self) -> &str {
53        std::any::type_name::<Self>()
54    }
55
56    /// Get module description
57    fn description(&self) -> Option<&str> {
58        None
59    }
60
61    /// Get module version
62    fn version(&self) -> Option<&str> {
63        None
64    }
65
66    /// Configure services for this module using ServiceBindings
67    fn configure(&self, services: &mut ServiceBindings) {
68        // Default implementation does nothing
69        // Modules should override this to register their services
70        let _ = services;
71    }
72
73    /// Get module dependencies (other modules this module depends on)
74    fn depends_on(&self) -> Vec<ModuleId> {
75        vec![]
76    }
77
78    /// Initialize module after all dependencies are loaded
79    fn initialize(
80        &self,
81        container: &IocContainer,
82    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), CoreError>> + Send + '_>>
83    {
84        let _ = container; // Default implementation does nothing
85        Box::pin(async move { Ok(()) })
86    }
87
88    /// Cleanup module resources
89    fn shutdown(
90        &self,
91        container: &IocContainer,
92    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<(), CoreError>> + Send + '_>>
93    {
94        let _ = container; // Default implementation does nothing
95        Box::pin(async move { Ok(()) })
96    }
97
98    /// Check if module is compatible with given version
99    fn is_compatible_with(&self, other_version: &str) -> bool {
100        let _ = other_version; // Default implementation is always compatible
101        true
102    }
103
104    /// Get module metadata
105    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/// Module metadata for introspection
120#[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/// Module configuration options
130#[derive(Debug, Clone)]
131pub struct ModuleConfig {
132    /// Whether to automatically initialize the module
133    pub auto_initialize: bool,
134    /// Initialization timeout
135    pub init_timeout: Option<std::time::Duration>,
136    /// Whether to validate dependencies before loading
137    pub validate_dependencies: bool,
138    /// Additional configuration parameters
139    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/// Module loading state
154#[derive(Debug, Clone, PartialEq)]
155pub enum ModuleState {
156    /// Module is registered but not configured
157    Registered,
158    /// Module services are configured
159    Configured,
160    /// Module is initialized and ready
161    Initialized,
162    /// Module initialization failed
163    Failed(String),
164    /// Module is shut down
165    Shutdown,
166}
167
168/// Information about a loaded module
169#[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
178/// Module registry for managing service modules
179// Debug removed due to dyn ServiceModule incompatibility
180pub 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    /// Create a new module registry
189    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    /// Register a module
199    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        // Check for duplicate registration
209        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        // Store module
216        self.modules.insert(module_id.clone(), module_arc);
217
218        // Store dependency info
219        self.dependency_graph
220            .insert(module_id.clone(), metadata.dependencies.clone());
221
222        // Create loaded module info
223        let loaded_module = LoadedModule {
224            metadata,
225            config: config.unwrap_or_default(),
226            state: ModuleState::Registered,
227            load_order: 0, // Will be set during load ordering
228            init_duration: None,
229        };
230
231        self.loaded_modules.insert(module_id, loaded_module);
232
233        Ok(())
234    }
235
236    /// Register multiple modules
237    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    /// Calculate load order based on dependencies
248    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        // Topological sort using DFS
254        // Sort module IDs to ensure consistent ordering
255        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        // No reverse needed - DFS post-order already gives correct dependency order
270        self.load_order = order.clone();
271
272        // Update load order in module info
273        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    /// Visit module for topological sorting (DFS)
283    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        // Visit dependencies first
306        if let Some(dependencies) = self.dependency_graph.get(module_id) {
307            for dep_id in dependencies {
308                // Check if dependency is registered
309                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    /// Configure all registered modules
330    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        // Collect all service bindings from all modules first
338        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            // Let module configure its services into bindings
350            module.configure(&mut bindings);
351
352            // Update state
353            if let Some(loaded_module) = self.loaded_modules.get_mut(module_id) {
354                loaded_module.state = ModuleState::Configured;
355            }
356        }
357
358        // Now register all collected bindings with the container using the new add_service_descriptor method
359        for descriptor in bindings.into_descriptors() {
360            container.add_service_descriptor(descriptor)?;
361        }
362
363        Ok(())
364    }
365
366    /// Initialize all modules in dependency order
367    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            // Get config for timeout
382            let config = self
383                .loaded_modules
384                .get(&module_id)
385                .map(|m| m.config.clone())
386                .unwrap_or_default();
387
388            // Initialize with timeout if specified
389            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            // Update module state
402            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    /// Shutdown all modules in reverse dependency order
417    pub async fn shutdown_all(&mut self, container: &IocContainer) -> Result<(), CoreError> {
418        let mut order = self.load_order.clone();
419        order.reverse(); // Shutdown in reverse order
420
421        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                // Continue with other modules even if one fails
437            }
438
439            // Update state
440            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    /// Get module by ID
449    pub fn get_module(&self, module_id: &ModuleId) -> Option<&Arc<dyn ServiceModule>> {
450        self.modules.get(module_id)
451    }
452
453    /// Get loaded module info
454    pub fn get_loaded_module(&self, module_id: &ModuleId) -> Option<&LoadedModule> {
455        self.loaded_modules.get(module_id)
456    }
457
458    /// Get all loaded modules
459    pub fn get_all_loaded_modules(&self) -> Vec<&LoadedModule> {
460        self.loaded_modules.values().collect()
461    }
462
463    /// Check if a module is loaded and initialized
464    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    /// Get module dependency graph for visualization
472    pub fn get_dependency_graph(&self) -> &HashMap<ModuleId, Vec<ModuleId>> {
473        &self.dependency_graph
474    }
475
476    /// Get load order
477    pub fn get_load_order(&self) -> &[ModuleId] {
478        &self.load_order
479    }
480
481    /// Validate all module dependencies
482    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
513/// Builder for modular container configuration
514pub 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    /// Create a new modular container builder
527    pub fn new(container: T) -> Self {
528        Self {
529            registry: ModuleRegistry::new(),
530            container,
531        }
532    }
533
534    /// Add a module
535    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    /// Add a module with configuration
541    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    /// Add multiple modules
551    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    /// Build the container with all modules configured
560    pub fn build(mut self) -> Result<(T, ModuleRegistry), CoreError> {
561        // Validate dependencies
562        self.registry
563            .validate_dependencies()
564            .map_err(|errors| errors.into_iter().next().unwrap())?; // Return first error
565
566        // Configure all modules
567        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    // Test modules
579    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            // No services to configure in test
592        }
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            // No services to configure in test
608        }
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            // No services to configure in test
624        }
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        // CoreModule should be first (no dependencies)
650        // AuthModule should be second (depends on Core)
651        // ApiModule should be last (depends on both)
652        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>()] // Circular dependency
673            }
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        // Verify load order was calculated
725        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        // Test module that actually registers services
733        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        // Configure modules with the builder
753        registry.configure_all(&mut container_builder).unwrap();
754
755        // Build the container
756        let container = container_builder.build().unwrap();
757
758        // Verify the service was registered and can be resolved
759        let service = container.resolve::<TestService>();
760        assert!(service.is_ok());
761    }
762}