elif_core/modules/
loader.rs

1use crate::container::{Container, ContainerBuilder};
2use crate::modules::{Module, ModuleError, ModuleRegistry};
3use std::time::Instant;
4
5/// Module loader for orchestrating module loading process
6pub struct ModuleLoader {
7    registry: ModuleRegistry,
8    loading_stats: LoadingStats,
9}
10
11impl ModuleLoader {
12    /// Create a new module loader
13    pub fn new() -> Self {
14        Self {
15            registry: ModuleRegistry::new(),
16            loading_stats: LoadingStats::new(),
17        }
18    }
19
20    /// Create a new module loader with existing registry
21    pub fn with_registry(registry: ModuleRegistry) -> Self {
22        Self {
23            registry,
24            loading_stats: LoadingStats::new(),
25        }
26    }
27
28    /// Register a module
29    pub fn register<M: Module + 'static>(&mut self, module: M) {
30        self.registry.register(module);
31    }
32
33    /// Register multiple modules at once
34    pub fn register_modules<M: Module + 'static>(&mut self, modules: Vec<M>) {
35        for module in modules {
36            self.registry.register(module);
37        }
38    }
39
40    /// Load all modules and create container
41    pub async fn load(&mut self) -> Result<Container, ModuleError> {
42        let start_time = Instant::now();
43
44        tracing::info!("Starting module loading process...");
45
46        // Validate modules
47        self.registry.validate()?;
48        self.loading_stats.validation_time = start_time.elapsed();
49
50        // Resolve dependencies
51        let dep_start = Instant::now();
52        self.registry.resolve_dependencies()?;
53        self.loading_stats.dependency_resolution_time = dep_start.elapsed();
54
55        // Configure container
56        let config_start = Instant::now();
57        let builder = ContainerBuilder::new();
58        let builder = self.registry.configure_all(builder)?;
59        let mut container = builder.build()?;
60        self.loading_stats.configuration_time = config_start.elapsed();
61
62        // Initialize container
63        let init_start = Instant::now();
64        container.initialize().await?;
65        self.loading_stats.initialization_time = init_start.elapsed();
66
67        // Boot modules
68        let boot_start = Instant::now();
69        self.registry.boot_all(&container)?;
70        self.loading_stats.boot_time = boot_start.elapsed();
71
72        self.loading_stats.total_time = start_time.elapsed();
73        self.loading_stats.module_count = self.registry.module_count();
74
75        tracing::info!(
76            "Module loading completed successfully in {:?} with {} modules",
77            self.loading_stats.total_time,
78            self.loading_stats.module_count
79        );
80
81        Ok(container)
82    }
83
84    /// Load modules without creating container (for testing)
85    pub fn load_modules_only(&mut self) -> Result<(), ModuleError> {
86        self.registry.validate()?;
87        self.registry.resolve_dependencies()?;
88        Ok(())
89    }
90
91    /// Get loading statistics
92    pub fn loading_stats(&self) -> &LoadingStats {
93        &self.loading_stats
94    }
95
96    /// Get module registry
97    pub fn registry(&self) -> &ModuleRegistry {
98        &self.registry
99    }
100
101    /// Get mutable module registry
102    pub fn registry_mut(&mut self) -> &mut ModuleRegistry {
103        &mut self.registry
104    }
105
106    /// Get all routes from loaded modules
107    pub fn routes(&mut self) -> Vec<crate::modules::RouteDefinition> {
108        self.registry.collect_routes()
109    }
110
111    /// Get all middleware from loaded modules
112    pub fn middleware(&mut self) -> Vec<crate::modules::MiddlewareDefinition> {
113        self.registry.collect_middleware()
114    }
115
116    /// Print loading summary
117    pub fn print_summary(&self) {
118        let stats = &self.loading_stats;
119
120        println!("\n=== Module Loading Summary ===");
121        println!("Modules loaded: {}", stats.module_count);
122        println!("Total time: {:?}", stats.total_time);
123        println!("  - Validation: {:?}", stats.validation_time);
124        println!(
125            "  - Dependency resolution: {:?}",
126            stats.dependency_resolution_time
127        );
128        println!("  - Configuration: {:?}", stats.configuration_time);
129        println!("  - Initialization: {:?}", stats.initialization_time);
130        println!("  - Boot: {:?}", stats.boot_time);
131        println!("===============================\n");
132    }
133}
134
135impl Default for ModuleLoader {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140
141/// Statistics for module loading process
142#[derive(Debug, Clone)]
143pub struct LoadingStats {
144    pub module_count: usize,
145    pub total_time: std::time::Duration,
146    pub validation_time: std::time::Duration,
147    pub dependency_resolution_time: std::time::Duration,
148    pub configuration_time: std::time::Duration,
149    pub initialization_time: std::time::Duration,
150    pub boot_time: std::time::Duration,
151}
152
153impl LoadingStats {
154    /// Create new loading stats
155    pub fn new() -> Self {
156        Self {
157            module_count: 0,
158            total_time: std::time::Duration::ZERO,
159            validation_time: std::time::Duration::ZERO,
160            dependency_resolution_time: std::time::Duration::ZERO,
161            configuration_time: std::time::Duration::ZERO,
162            initialization_time: std::time::Duration::ZERO,
163            boot_time: std::time::Duration::ZERO,
164        }
165    }
166
167    /// Get the percentage of time spent on each phase
168    pub fn phase_percentages(&self) -> PhasePercentages {
169        let total_micros = self.total_time.as_micros() as f64;
170
171        if total_micros == 0.0 {
172            return PhasePercentages::default();
173        }
174
175        PhasePercentages {
176            validation: (self.validation_time.as_micros() as f64 / total_micros) * 100.0,
177            dependency_resolution: (self.dependency_resolution_time.as_micros() as f64
178                / total_micros)
179                * 100.0,
180            configuration: (self.configuration_time.as_micros() as f64 / total_micros) * 100.0,
181            initialization: (self.initialization_time.as_micros() as f64 / total_micros) * 100.0,
182            boot: (self.boot_time.as_micros() as f64 / total_micros) * 100.0,
183        }
184    }
185}
186
187impl Default for LoadingStats {
188    fn default() -> Self {
189        Self::new()
190    }
191}
192
193/// Phase percentages for loading stats
194#[derive(Debug, Clone, Default)]
195pub struct PhasePercentages {
196    pub validation: f64,
197    pub dependency_resolution: f64,
198    pub configuration: f64,
199    pub initialization: f64,
200    pub boot: f64,
201}
202
203/// Builder for module loader
204pub struct ModuleLoaderBuilder {
205    loader: ModuleLoader,
206}
207
208impl ModuleLoaderBuilder {
209    /// Create a new module loader builder
210    pub fn new() -> Self {
211        Self {
212            loader: ModuleLoader::new(),
213        }
214    }
215
216    /// Add a module
217    pub fn add_module<M: Module + 'static>(mut self, module: M) -> Self {
218        self.loader.register(module);
219        self
220    }
221
222    /// Add multiple modules
223    pub fn add_modules<M: Module + 'static>(mut self, modules: Vec<M>) -> Self {
224        self.loader.register_modules(modules);
225        self
226    }
227
228    /// Build and return the module loader
229    pub fn build(self) -> ModuleLoader {
230        self.loader
231    }
232
233    /// Build, load modules, and return container
234    pub async fn load(mut self) -> Result<Container, ModuleError> {
235        self.loader.load().await
236    }
237}
238
239impl Default for ModuleLoaderBuilder {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248    use crate::modules::BaseModule;
249
250    #[tokio::test]
251    async fn test_module_loader() -> Result<(), ModuleError> {
252        let mut loader = ModuleLoader::new();
253
254        loader.register(BaseModule::new("test_module_1"));
255        loader.register(BaseModule::new("test_module_2"));
256
257        let container = loader.load().await?;
258
259        assert!(container.is_initialized());
260        assert_eq!(loader.loading_stats().module_count, 2);
261
262        Ok(())
263    }
264
265    #[tokio::test]
266    async fn test_module_loader_builder() -> Result<(), ModuleError> {
267        let container = ModuleLoaderBuilder::new()
268            .add_module(BaseModule::new("test_module_1"))
269            .add_module(BaseModule::new("test_module_2"))
270            .load()
271            .await?;
272
273        assert!(container.is_initialized());
274
275        Ok(())
276    }
277}