elif_http/bootstrap/
engine.rs

1//! Bootstrap engine that orchestrates module discovery, dependency resolution,
2//! container configuration, and server startup.
3
4use crate::{
5    bootstrap::{BootstrapError, BootstrapResult, ControllerRegistry},
6    config::HttpConfig,
7    routing::ElifRouter,
8    server::Server,
9    Middleware,
10};
11use elif_core::{
12    container::IocContainer,
13    modules::{get_global_module_registry, CompileTimeModuleMetadata, ModuleDescriptor, ModuleRuntime, ModuleRuntimeError},
14};
15use std::{future::Future, net::SocketAddr, pin::Pin, sync::Arc};
16
17/// The main bootstrap orchestrator that handles the complete app startup process
18///
19/// This struct provides a fluent API for configuring and starting an elif.rs application:
20/// - Discovers all modules automatically via the compile-time registry
21/// - Resolves module dependencies using ModuleRuntime with enhanced error handling
22/// - Configures the DI container with all providers
23/// - Registers all controllers and their routes
24/// - Sets up middleware pipeline
25/// - Starts the HTTP server
26#[derive(Debug)]
27pub struct AppBootstrapper {
28    /// Discovered module metadata from compile-time registry
29    modules: Vec<CompileTimeModuleMetadata>,
30    /// Runtime module system for enhanced dependency resolution and lifecycle management
31    module_runtime: ModuleRuntime,
32    /// HTTP server configuration
33    config: HttpConfig,
34    /// Middleware stack to apply
35    middleware: Vec<Box<dyn Middleware>>,
36    /// Custom DI container if provided
37    container: Option<IocContainer>,
38}
39
40impl AppBootstrapper {
41    /// Create a new AppBootstrapper with automatic module discovery
42    ///
43    /// This method:
44    /// 1. Gets all modules from the global compile-time registry
45    /// 2. Converts them to ModuleDescriptors for ModuleRuntime
46    /// 3. Uses ModuleRuntime for enhanced dependency resolution and lifecycle management
47    /// 4. Sets up default configuration
48    pub fn new() -> BootstrapResult<Self> {
49        let registry = get_global_module_registry();
50        
51        // Get all modules from compile-time registry
52        let modules: Vec<CompileTimeModuleMetadata> = registry.all_modules()
53            .into_iter()
54            .cloned()
55            .collect();
56        
57        if modules.is_empty() {
58            return Err(BootstrapError::ModuleDiscoveryFailed {
59                message: "No modules found. Make sure you have modules decorated with #[module]".to_string(),
60            });
61        }
62        
63        // Create ModuleRuntime and register all modules
64        let mut module_runtime = ModuleRuntime::new();
65        for module_metadata in &modules {
66            let descriptor = Self::convert_metadata_to_descriptor(module_metadata);
67            module_runtime.register_module(descriptor)
68                .map_err(|e| BootstrapError::ModuleRegistrationFailed {
69                    message: format!("Failed to register module '{}': {}", module_metadata.name, e),
70                })?;
71        }
72        
73        // Calculate load order using ModuleRuntime's sophisticated dependency resolution
74        let load_order = module_runtime.calculate_load_order()
75            .map_err(|e| match e {
76                ModuleRuntimeError::CircularDependency { cycle, message: _ } => {
77                    BootstrapError::CircularDependency { cycle }
78                }
79                ModuleRuntimeError::MissingDependency { module, missing_dependency, message: _ } => {
80                    BootstrapError::MissingDependency {
81                        module,
82                        dependency: missing_dependency,
83                    }
84                }
85                other => BootstrapError::ModuleRegistrationFailed {
86                    message: format!("Module dependency resolution failed: {}", other),
87                }
88            })?;
89        
90        tracing::info!("Bootstrap: Discovered {} modules", modules.len());
91        tracing::info!("Bootstrap: Load order resolved: {:?}", load_order);
92        
93        Ok(AppBootstrapper {
94            modules,
95            module_runtime,
96            config: HttpConfig::default(),
97            middleware: Vec::new(),
98            container: None,
99        })
100    }
101    
102    /// Convert CompileTimeModuleMetadata to ModuleDescriptor for ModuleRuntime
103    fn convert_metadata_to_descriptor(metadata: &CompileTimeModuleMetadata) -> ModuleDescriptor {
104        ModuleDescriptor {
105            name: metadata.name.clone(),
106            version: None,
107            description: None,
108            providers: Vec::new(), // CompileTimeModuleMetadata only has provider names, not full descriptors
109            controllers: Vec::new(), // CompileTimeModuleMetadata only has controller names, not full descriptors
110            imports: metadata.imports.clone(),
111            exports: metadata.exports.clone(),
112            dependencies: metadata.imports.clone(), // In this context, imports are dependencies
113            is_optional: false,
114        }
115    }
116    
117    /// Configure the HTTP server with custom configuration
118    pub fn with_config(mut self, config: HttpConfig) -> Self {
119        self.config = config;
120        self
121    }
122    
123    /// Add middleware to the application
124    pub fn with_middleware(mut self, middleware: Vec<Box<dyn Middleware>>) -> Self {
125        self.middleware = middleware;
126        self
127    }
128    
129    /// Use a pre-configured DI container
130    pub fn with_container(mut self, container: IocContainer) -> Self {
131        self.container = Some(container);
132        self
133    }
134    
135    /// Start the HTTP server on the specified address
136    ///
137    /// This method performs the complete bootstrap sequence:
138    /// 1. Configures the DI container with all module providers using ModuleRuntime
139    /// 2. Creates and configures the router with all controller routes
140    /// 3. Sets up the middleware pipeline
141    /// 4. Starts the HTTP server
142    pub fn listen(
143        mut self,
144        addr: impl Into<SocketAddr> + Send + 'static,
145    ) -> Pin<Box<dyn Future<Output = BootstrapResult<()>> + Send>> {
146        Box::pin(async move {
147            let addr = addr.into();
148            
149            tracing::info!("Bootstrap: Starting server on {}", addr);
150            
151            // Step 1: Configure DI container using ModuleRuntime
152            let container = self.configure_container().await?;
153            
154            // Step 2: Create and configure router with container access
155            let router = self.configure_router(&container).await?;
156            
157            // Step 3: Create and start server
158            // Note: Creating a new container for server since IocContainer doesn't implement Clone
159            // This will be enhanced once the IoC container supports better sharing patterns
160            let server_container = IocContainer::new();
161            let mut server = Server::new(server_container, self.config)
162                .map_err(|e| BootstrapError::ServerStartupFailed {
163                    message: format!("Failed to create server: {}", e),
164                })?;
165            
166            // Apply middleware and use router
167            if !self.middleware.is_empty() {
168                // TODO: Apply middleware to server
169                // For now, just use the server as-is
170            }
171            server.use_router(router);
172            
173            // Start listening - convert SocketAddr to string
174            server
175                .listen(addr.to_string())
176                .await
177                .map_err(|e| BootstrapError::ServerStartupFailed {
178                    message: format!("Failed to start server: {}", e),
179                })?;
180            
181            Ok(())
182        })
183    }
184    
185    /// Configure the DI container with all module providers using ModuleRuntime
186    async fn configure_container(&mut self) -> BootstrapResult<Arc<IocContainer>> {
187        let mut container = if let Some(_provided_container) = &self.container {
188            // TODO: Proper container merging/extension when IocContainer supports it
189            // For now, we create a new container and document this limitation
190            tracing::warn!("Bootstrap: Custom container provided but cannot be cloned. Creating new container with module configurations.");
191            tracing::info!("Bootstrap: To properly use custom containers, consider configuring modules directly on your container before bootstrap");
192            IocContainer::new()
193        } else {
194            IocContainer::new()
195        };
196        
197        // Use ModuleRuntime's sophisticated dependency resolution and container configuration
198        self.module_runtime.resolve_dependencies(&mut container)
199            .await
200            .map_err(|e| BootstrapError::ContainerConfigurationFailed {
201                message: format!("ModuleRuntime dependency resolution failed: {}", e),
202            })?;
203        
204        tracing::info!("Bootstrap: Container configured with {} modules using ModuleRuntime", self.modules.len());
205        Ok(Arc::new(container))
206    }
207    
208    /// Configure the router with all controller routes
209    async fn configure_router(&self, container: &Arc<IocContainer>) -> BootstrapResult<ElifRouter> {
210        let mut router = ElifRouter::new();
211        
212        // Create ControllerRegistry with IoC container access
213        let controller_registry = ControllerRegistry::from_modules(&self.modules, container.clone())
214            .map_err(|e| BootstrapError::ControllerRegistrationFailed {
215                message: format!("Failed to create controller registry: {}", e),
216            })?;
217        
218        tracing::info!("Bootstrap: Created controller registry with {} controllers", 
219                      controller_registry.get_controller_names().len());
220        
221        // Validate all routes for conflicts before registration
222        if let Err(conflicts) = controller_registry.validate_routes() {
223            let mut error_message = String::new();
224            for conflict in &conflicts {
225                error_message.push_str(&format!("\n  - {}/{}: {} vs {}/{}", 
226                    conflict.route1.method, conflict.route1.path,
227                    conflict.route1.controller,
228                    conflict.route2.method, conflict.route2.path));
229            }
230            
231            return Err(BootstrapError::RouteRegistrationFailed {
232                message: format!("Route conflicts detected:{}", error_message),
233            });
234        }
235        
236        // Register all controllers automatically
237        router = controller_registry.register_all_routes(router)
238            .map_err(|e| BootstrapError::ControllerRegistrationFailed {
239                message: format!("Failed to register controller routes: {}", e),
240            })?;
241        
242        tracing::info!("Bootstrap: Successfully registered {} controller routes", 
243                      controller_registry.total_routes());
244        tracing::info!("Bootstrap: Router configured with controllers from {} modules", self.modules.len());
245        
246        Ok(router)
247    }
248    
249    /// Get discovered modules (for debugging/introspection)
250    pub fn modules(&self) -> &[CompileTimeModuleMetadata] {
251        &self.modules
252    }
253    
254    /// Get module load order from ModuleRuntime (for debugging/introspection)
255    pub fn load_order(&self) -> &[String] {
256        self.module_runtime.load_order()
257    }
258}
259
260impl Default for AppBootstrapper {
261    fn default() -> Self {
262        Self::new().unwrap_or_else(|e| {
263            tracing::error!("Failed to create default AppBootstrapper: {}", e);
264            panic!("Failed to create default AppBootstrapper: {}", e);
265        })
266    }
267}
268
269/// Utility function to create a bootstrapper directly (for convenience)
270pub fn create_bootstrapper() -> BootstrapResult<AppBootstrapper> {
271    AppBootstrapper::new()
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277    use elif_core::modules::{register_module_globally, CompileTimeModuleMetadata};
278    
279    #[tokio::test]
280    async fn test_bootstrap_creation() {
281        // Register a test module
282        let test_module = CompileTimeModuleMetadata::new("TestModule".to_string())
283            .with_controller("TestController".to_string())
284            .with_provider("TestService".to_string());
285        
286        register_module_globally(test_module);
287        
288        let bootstrapper = AppBootstrapper::new().expect("Should create bootstrapper");
289        
290        assert!(!bootstrapper.modules().is_empty());
291        assert!(bootstrapper.modules().iter().any(|m| m.name == "TestModule"));
292    }
293    
294    #[tokio::test]
295    async fn test_bootstrap_configuration() {
296        let test_module = CompileTimeModuleMetadata::new("ConfigTestModule".to_string());
297        register_module_globally(test_module);
298        
299        let config = HttpConfig::default();
300        let bootstrapper = AppBootstrapper::new()
301            .expect("Should create bootstrapper")
302            .with_config(config);
303        
304        // Just test that configuration doesn't panic
305        assert!(!bootstrapper.modules().is_empty());
306    }
307    
308    #[tokio::test]
309    async fn test_bootstrap_error_handling() {
310        // Test what happens when no modules are registered
311        // Note: This test may be affected by other tests registering modules
312        // In a real scenario, you'd want to use a separate test registry
313        
314        // The current implementation will find modules from other tests,
315        // but in principle, if no modules were found, it should return an error
316        let result = AppBootstrapper::new();
317        
318        // Either succeeds (because other tests registered modules) 
319        // or fails with a clear error message
320        match result {
321            Ok(bootstrapper) => {
322                // Other tests registered modules, that's fine
323                assert!(!bootstrapper.modules().is_empty());
324            }
325            Err(BootstrapError::ModuleDiscoveryFailed { message }) => {
326                // This is the expected error when no modules are found
327                assert!(message.contains("No modules found"));
328            }
329            Err(other) => {
330                panic!("Unexpected error type: {:?}", other);
331            }
332        }
333    }
334}