elif_http/bootstrap/
engine.rs1use 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#[derive(Debug)]
27pub struct AppBootstrapper {
28 modules: Vec<CompileTimeModuleMetadata>,
30 module_runtime: ModuleRuntime,
32 config: HttpConfig,
34 middleware: Vec<Box<dyn Middleware>>,
36 container: Option<IocContainer>,
38}
39
40impl AppBootstrapper {
41 pub fn new() -> BootstrapResult<Self> {
49 let registry = get_global_module_registry();
50
51 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 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 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 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(), controllers: Vec::new(), imports: metadata.imports.clone(),
111 exports: metadata.exports.clone(),
112 dependencies: metadata.imports.clone(), is_optional: false,
114 }
115 }
116
117 pub fn with_config(mut self, config: HttpConfig) -> Self {
119 self.config = config;
120 self
121 }
122
123 pub fn with_middleware(mut self, middleware: Vec<Box<dyn Middleware>>) -> Self {
125 self.middleware = middleware;
126 self
127 }
128
129 pub fn with_container(mut self, container: IocContainer) -> Self {
131 self.container = Some(container);
132 self
133 }
134
135 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 let container = self.configure_container().await?;
153
154 let router = self.configure_router(&container).await?;
156
157 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 if !self.middleware.is_empty() {
168 }
171 server.use_router(router);
172
173 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 async fn configure_container(&mut self) -> BootstrapResult<Arc<IocContainer>> {
187 let mut container = if let Some(_provided_container) = &self.container {
188 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 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 async fn configure_router(&self, container: &Arc<IocContainer>) -> BootstrapResult<ElifRouter> {
210 let mut router = ElifRouter::new();
211
212 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 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 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 pub fn modules(&self) -> &[CompileTimeModuleMetadata] {
251 &self.modules
252 }
253
254 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
269pub 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 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 assert!(!bootstrapper.modules().is_empty());
306 }
307
308 #[tokio::test]
309 async fn test_bootstrap_error_handling() {
310 let result = AppBootstrapper::new();
317
318 match result {
321 Ok(bootstrapper) => {
322 assert!(!bootstrapper.modules().is_empty());
324 }
325 Err(BootstrapError::ModuleDiscoveryFailed { message }) => {
326 assert!(message.contains("No modules found"));
328 }
329 Err(other) => {
330 panic!("Unexpected error type: {:?}", other);
331 }
332 }
333 }
334}