1use super::application::BootApplication;
2use super::lazy::LazyModuleLoader;
3use super::registration::{ModuleRegistrationSink, ModuleRegistry};
4use crate::pipeline::PipelineComponents;
5#[cfg(feature = "auth")]
6use crate::AuthGuard;
7use crate::{
8 ApiVersioning, BootError, BootErrorKind, BootResponse, ExceptionFilter, ExecutionInterceptor,
9 Guard, Interceptor, MessagePatternDefinition, Middleware, Module, ModuleRef, OpenApiDocument,
10 OpenApiInfo, Pipe, ProviderDefinition, ProviderToken, Result, RouteDefinition,
11 SerializationInterceptor, WebSocketGatewayDefinition,
12};
13#[cfg(feature = "compression")]
14use crate::{CompressionInterceptor, CompressionOptions};
15#[cfg(feature = "security")]
16use crate::{
17 CorsMiddleware, CorsOptions, CorsPreflightRoute, CorsResponseInterceptor, CsrfGuard,
18 CsrfOptions, RateLimitGuard, RateLimitOptions, SecurityHeadersInterceptor,
19 SecurityHeadersOptions,
20};
21#[cfg(feature = "session")]
22use crate::{SessionCookieInterceptor, SessionManager, SessionMiddleware, SessionModule};
23use std::collections::{BTreeMap, BTreeSet};
24use std::sync::Arc;
25
26#[derive(Default)]
28pub struct BootApplicationBuilder {
29 modules: Vec<Arc<dyn Module>>,
30 routes: Vec<RouteDefinition>,
31 gateways: Vec<WebSocketGatewayDefinition>,
32 message_patterns: Vec<MessagePatternDefinition>,
33 global_pipeline: PipelineComponents,
34 global_execution_guards: Vec<Arc<dyn Guard>>,
35 global_execution_interceptors: Vec<Arc<dyn ExecutionInterceptor>>,
36 global_prefix: Option<String>,
37 api_versioning: Option<ApiVersioning>,
38 openapi_routes: Vec<(String, OpenApiInfo)>,
39 provider_overrides: BTreeMap<ProviderToken, ProviderDefinition>,
40 #[cfg(feature = "security")]
41 cors_preflight: Option<CorsPreflightRoute>,
42}
43
44impl BootApplicationBuilder {
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn import<M>(mut self, module: M) -> Self
51 where
52 M: Module,
53 {
54 self.modules.push(Arc::new(module));
55 self
56 }
57
58 pub fn import_arc(mut self, module: Arc<dyn Module>) -> Self {
60 self.modules.push(module);
61 self
62 }
63
64 pub fn route(mut self, route: RouteDefinition) -> Self {
66 self.routes.push(route);
67 self
68 }
69
70 pub fn gateway(mut self, gateway: WebSocketGatewayDefinition) -> Self {
72 self.gateways.push(gateway);
73 self
74 }
75
76 pub fn message_pattern(mut self, pattern: MessagePatternDefinition) -> Self {
78 self.message_patterns.push(pattern);
79 self
80 }
81
82 pub fn serve_openapi(mut self, path: impl Into<String>, info: OpenApiInfo) -> Self {
84 self.openapi_routes.push((path.into(), info));
85 self
86 }
87
88 pub fn global_prefix(mut self, prefix: impl Into<String>) -> Self {
90 self.global_prefix = Some(prefix.into());
91 self
92 }
93
94 pub fn enable_api_versioning(mut self, versioning: ApiVersioning) -> Self {
96 self.api_versioning = Some(versioning);
97 self
98 }
99
100 pub fn use_global_middleware<M>(mut self, middleware: M) -> Self
102 where
103 M: Middleware,
104 {
105 self.global_pipeline.push_middleware(middleware);
106 self
107 }
108
109 pub fn use_global_pipe<P>(mut self, pipe: P) -> Self
111 where
112 P: Pipe,
113 {
114 self.global_pipeline.push_pipe(pipe);
115 self
116 }
117
118 pub fn use_global_guard<G>(mut self, guard: G) -> Self
120 where
121 G: Guard,
122 {
123 self.global_pipeline.push_guard(guard);
124 self
125 }
126
127 pub fn use_global_execution_guard<G>(mut self, guard: G) -> Self
129 where
130 G: Guard,
131 {
132 let guard: Arc<dyn Guard> = Arc::new(guard);
133 self.global_pipeline.push_guard_arc(Arc::clone(&guard));
134 self.global_execution_guards.push(guard);
135 self
136 }
137
138 #[cfg(feature = "auth")]
140 pub fn use_global_auth(mut self) -> Self {
141 self.global_pipeline.push_guard(AuthGuard::new());
142 self
143 }
144
145 #[cfg(feature = "auth")]
147 pub fn use_global_auth_strategy(mut self, strategy: impl Into<String>) -> Self {
148 self.global_pipeline
149 .push_guard(AuthGuard::new().strategy(strategy));
150 self
151 }
152
153 pub fn use_global_interceptor<I>(mut self, interceptor: I) -> Self
155 where
156 I: Interceptor,
157 {
158 self.global_pipeline.push_interceptor(interceptor);
159 self
160 }
161
162 pub fn use_global_execution_interceptor<I>(mut self, interceptor: I) -> Self
164 where
165 I: ExecutionInterceptor,
166 {
167 let interceptor: Arc<dyn ExecutionInterceptor> = Arc::new(interceptor);
168 self.global_pipeline
169 .push_execution_interceptor_arc(Arc::clone(&interceptor));
170 self.global_execution_interceptors.push(interceptor);
171 self
172 }
173
174 pub fn use_global_serialization(mut self) -> Self {
176 self.global_pipeline
177 .push_interceptor(SerializationInterceptor::new());
178 self
179 }
180
181 #[cfg(feature = "compression")]
183 pub fn use_global_compression(mut self, options: CompressionOptions) -> Self {
184 self.global_pipeline
185 .push_interceptor(CompressionInterceptor::with_options(options));
186 self
187 }
188
189 #[cfg(feature = "security")]
191 pub fn use_global_cors(mut self, options: CorsOptions) -> Self {
192 self.global_pipeline
193 .push_middleware(CorsMiddleware::with_options(options.clone()));
194 self.global_pipeline
195 .push_interceptor(CorsResponseInterceptor::with_options(options.clone()));
196 self.cors_preflight = Some(CorsPreflightRoute::with_options(options));
197 self
198 }
199
200 #[cfg(feature = "security")]
202 pub fn use_global_security_headers(mut self, options: SecurityHeadersOptions) -> Self {
203 self.global_pipeline
204 .push_interceptor(SecurityHeadersInterceptor::with_options(options));
205 self
206 }
207
208 #[cfg(feature = "security")]
210 pub fn use_global_csrf(mut self, options: CsrfOptions) -> Self {
211 self.global_pipeline
212 .push_guard(CsrfGuard::with_options(options));
213 self
214 }
215
216 #[cfg(feature = "security")]
218 pub fn use_global_rate_limit(mut self, options: RateLimitOptions) -> Self {
219 self.global_pipeline
220 .push_guard(RateLimitGuard::with_options(options));
221 self
222 }
223
224 #[cfg(feature = "session")]
226 pub fn use_global_sessions(mut self, manager: SessionManager) -> Self {
227 self.global_pipeline
228 .push_middleware(SessionMiddleware::new(manager.clone()));
229 self.global_pipeline
230 .push_interceptor(SessionCookieInterceptor::new(manager));
231 self
232 }
233
234 #[cfg(feature = "session")]
236 pub fn use_global_session_module(mut self, module: SessionModule) -> Self {
237 let manager = module.manager();
238 self = self.use_global_sessions(manager);
239 self.modules.push(Arc::new(module));
240 self
241 }
242
243 pub fn use_global_filter<F>(mut self, filter: F) -> Self
245 where
246 F: ExceptionFilter,
247 {
248 self.global_pipeline.push_filter(filter);
249 self
250 }
251
252 pub fn use_global_catch_filter<I, F>(mut self, kinds: I, filter: F) -> Self
254 where
255 I: IntoIterator<Item = BootErrorKind>,
256 F: ExceptionFilter,
257 {
258 self.global_pipeline.push_catch_filter(kinds, filter);
259 self
260 }
261
262 pub fn use_global_validation(mut self) -> Self {
264 self.global_pipeline.enable_validation();
265 self
266 }
267
268 pub fn override_provider(mut self, provider: ProviderDefinition) -> Self {
273 self.provider_overrides
274 .insert(provider.token().clone(), provider);
275 self
276 }
277
278 pub fn build(self) -> Result<BootApplication> {
280 let module_ref = ModuleRef::new();
281 let global_ref = ModuleRef::new();
282 let lazy_module_loader = LazyModuleLoader::new(global_ref.clone());
283 global_ref.insert_arc(Arc::new(lazy_module_loader.clone()))?;
284 module_ref.add_visible_scope(global_ref.clone())?;
285 let mut registry = ModuleRegistry::new(global_ref, self.provider_overrides);
286 let mut modules = Vec::new();
287 let mut module_instances = Vec::new();
288 let mut routes = self
289 .routes
290 .into_iter()
291 .map(|route| route.with_pipeline_prefix(&self.global_pipeline))
292 .collect::<Vec<_>>();
293 let mut gateways = self.gateways;
294 let mut message_patterns = self.message_patterns;
295
296 {
297 let mut sink = ModuleRegistrationSink {
298 modules: &mut modules,
299 module_instances: &mut module_instances,
300 routes: &mut routes,
301 gateways: &mut gateways,
302 message_patterns: &mut message_patterns,
303 };
304
305 for module in &self.modules {
306 let registered = registry.register_module(
307 Arc::clone(module),
308 &self.global_pipeline,
309 &mut sink,
310 )?;
311 module_ref.add_visible_scope(registered.module_ref)?;
312 }
313 }
314 for (name, registered) in registry.registered_modules() {
315 lazy_module_loader.seed_module(name, registered.module_ref, registered.exports)?;
316 }
317
318 let mut routes = apply_global_prefix(routes, self.global_prefix.as_deref())?;
319 let gateways = apply_global_gateway_prefix(gateways, self.global_prefix.as_deref())?
320 .into_iter()
321 .map(|gateway| {
322 gateway.with_execution_pipeline_prefix(
323 &self.global_execution_guards,
324 &self.global_execution_interceptors,
325 )
326 })
327 .collect::<Vec<_>>();
328 let message_patterns = message_patterns
329 .into_iter()
330 .map(|pattern| {
331 pattern.with_execution_pipeline_prefix(
332 &self.global_execution_guards,
333 &self.global_execution_interceptors,
334 )
335 })
336 .collect::<Vec<_>>();
337 let documented_routes = routes.clone();
338
339 for (path, info) in self.openapi_routes {
340 let document = OpenApiDocument::from_routes(info, &documented_routes);
341 let route =
342 openapi_json_route(path, document)?.with_pipeline_prefix(&self.global_pipeline);
343 let route = match self.global_prefix.as_deref() {
344 Some(prefix) => route.with_path_prefix(prefix)?,
345 None => route,
346 };
347 routes.push(route);
348 }
349
350 #[cfg(feature = "security")]
351 if let Some(cors_preflight) = &self.cors_preflight {
352 add_cors_preflight_routes(&mut routes, cors_preflight, &self.global_pipeline)?;
353 }
354
355 routes = routes
356 .into_iter()
357 .map(|route| route.with_default_module_ref(module_ref.clone()))
358 .collect();
359
360 validate_unique_routes(&routes, self.api_versioning.as_ref())?;
361 validate_unique_gateways(&gateways)?;
362 validate_unique_message_patterns(&message_patterns)?;
363 validate_gateway_route_conflicts(&routes, &gateways)?;
364
365 Ok(BootApplication {
366 routes,
367 gateways,
368 message_patterns,
369 modules,
370 module_ref,
371 module_instances,
372 api_versioning: self.api_versioning,
373 })
374 }
375
376 pub async fn build_async(self) -> Result<BootApplication> {
378 let module_ref = ModuleRef::new();
379 let global_ref = ModuleRef::new();
380 let lazy_module_loader = LazyModuleLoader::new(global_ref.clone());
381 global_ref.insert_arc(Arc::new(lazy_module_loader.clone()))?;
382 module_ref.add_visible_scope(global_ref.clone())?;
383 let mut registry = ModuleRegistry::new(global_ref, self.provider_overrides);
384 let mut modules = Vec::new();
385 let mut module_instances = Vec::new();
386 let mut routes = self
387 .routes
388 .into_iter()
389 .map(|route| route.with_pipeline_prefix(&self.global_pipeline))
390 .collect::<Vec<_>>();
391 let mut gateways = self.gateways;
392 let mut message_patterns = self.message_patterns;
393
394 {
395 let mut sink = ModuleRegistrationSink {
396 modules: &mut modules,
397 module_instances: &mut module_instances,
398 routes: &mut routes,
399 gateways: &mut gateways,
400 message_patterns: &mut message_patterns,
401 };
402
403 for module in &self.modules {
404 let registered = registry
405 .register_module_async(Arc::clone(module), &self.global_pipeline, &mut sink)
406 .await?;
407 module_ref.add_visible_scope(registered.module_ref)?;
408 }
409 }
410 for (name, registered) in registry.registered_modules() {
411 lazy_module_loader.seed_module(name, registered.module_ref, registered.exports)?;
412 }
413
414 let mut routes = apply_global_prefix(routes, self.global_prefix.as_deref())?;
415 let gateways = apply_global_gateway_prefix(gateways, self.global_prefix.as_deref())?
416 .into_iter()
417 .map(|gateway| {
418 gateway.with_execution_pipeline_prefix(
419 &self.global_execution_guards,
420 &self.global_execution_interceptors,
421 )
422 })
423 .collect::<Vec<_>>();
424 let message_patterns = message_patterns
425 .into_iter()
426 .map(|pattern| {
427 pattern.with_execution_pipeline_prefix(
428 &self.global_execution_guards,
429 &self.global_execution_interceptors,
430 )
431 })
432 .collect::<Vec<_>>();
433 let documented_routes = routes.clone();
434
435 for (path, info) in self.openapi_routes {
436 let document = OpenApiDocument::from_routes(info, &documented_routes);
437 let route =
438 openapi_json_route(path, document)?.with_pipeline_prefix(&self.global_pipeline);
439 let route = match self.global_prefix.as_deref() {
440 Some(prefix) => route.with_path_prefix(prefix)?,
441 None => route,
442 };
443 routes.push(route);
444 }
445
446 #[cfg(feature = "security")]
447 if let Some(cors_preflight) = &self.cors_preflight {
448 add_cors_preflight_routes(&mut routes, cors_preflight, &self.global_pipeline)?;
449 }
450
451 routes = routes
452 .into_iter()
453 .map(|route| route.with_default_module_ref(module_ref.clone()))
454 .collect();
455
456 validate_unique_routes(&routes, self.api_versioning.as_ref())?;
457 validate_unique_gateways(&gateways)?;
458 validate_unique_message_patterns(&message_patterns)?;
459 validate_gateway_route_conflicts(&routes, &gateways)?;
460
461 Ok(BootApplication {
462 routes,
463 gateways,
464 message_patterns,
465 modules,
466 module_ref,
467 module_instances,
468 api_versioning: self.api_versioning,
469 })
470 }
471}
472
473fn apply_global_gateway_prefix(
474 gateways: Vec<WebSocketGatewayDefinition>,
475 prefix: Option<&str>,
476) -> Result<Vec<WebSocketGatewayDefinition>> {
477 match prefix {
478 Some(prefix) => gateways
479 .into_iter()
480 .map(|gateway| gateway.with_path_prefix(prefix))
481 .collect(),
482 None => Ok(gateways),
483 }
484}
485
486fn openapi_json_route(path: String, document: OpenApiDocument) -> Result<RouteDefinition> {
487 RouteDefinition::get(path, move |_| {
488 let document = document.clone();
489 async move { BootResponse::json(&document) }
490 })
491 .map(RouteDefinition::hide_from_openapi)
492}
493
494#[cfg(feature = "security")]
495fn add_cors_preflight_routes(
496 routes: &mut Vec<RouteDefinition>,
497 cors_preflight: &CorsPreflightRoute,
498 pipeline: &PipelineComponents,
499) -> Result<()> {
500 let existing_options = routes
501 .iter()
502 .filter(|route| route.method() == crate::HttpMethod::Options)
503 .map(RouteDefinition::path_shape)
504 .collect::<BTreeSet<_>>();
505 let mut generated = BTreeSet::new();
506 let source_routes = routes.clone();
507
508 for route in source_routes {
509 if route.method() == crate::HttpMethod::Options {
510 continue;
511 }
512
513 let path_shape = route.path_shape();
514 if existing_options.contains(&path_shape) || !generated.insert(path_shape) {
515 continue;
516 }
517
518 let preflight = RouteDefinition::options(route.path().to_string(), cors_preflight.clone())?
519 .hide_from_openapi()
520 .with_pipeline_prefix(pipeline);
521 routes.push(preflight);
522 }
523
524 Ok(())
525}
526
527fn apply_global_prefix(
528 routes: Vec<RouteDefinition>,
529 prefix: Option<&str>,
530) -> Result<Vec<RouteDefinition>> {
531 match prefix {
532 Some(prefix) => routes
533 .into_iter()
534 .map(|route| route.with_path_prefix(prefix))
535 .collect(),
536 None => Ok(routes),
537 }
538}
539
540fn validate_unique_routes(
541 routes: &[RouteDefinition],
542 versioning: Option<&ApiVersioning>,
543) -> Result<()> {
544 for (index, route) in routes.iter().enumerate() {
545 for existing in routes.iter().take(index) {
546 if existing.method() != route.method()
547 || existing.path_shape_key() != route.path_shape_key()
548 || existing.host_shape_key() != route.host_shape_key()
549 {
550 continue;
551 }
552
553 let duplicate = match versioning {
554 Some(versioning) => existing
555 .versioning()
556 .overlaps(route.versioning(), versioning.default_version()),
557 None => true,
558 };
559 if !duplicate {
560 continue;
561 }
562
563 let host = route
564 .host()
565 .map(|host| format!(" host {host}"))
566 .unwrap_or_default();
567 return Err(BootError::DuplicateRoute(format!(
568 "{} {}{} version {}",
569 route.method().as_str(),
570 route.path(),
571 host,
572 route.versioning()
573 )));
574 }
575 }
576
577 Ok(())
578}
579
580fn validate_unique_gateways(gateways: &[WebSocketGatewayDefinition]) -> Result<()> {
581 let mut seen = BTreeSet::new();
582
583 for gateway in gateways {
584 if !seen.insert(gateway.path_shape()) {
585 return Err(BootError::DuplicateRoute(format!("WS {}", gateway.path())));
586 }
587 }
588
589 Ok(())
590}
591
592fn validate_gateway_route_conflicts(
593 routes: &[RouteDefinition],
594 gateways: &[WebSocketGatewayDefinition],
595) -> Result<()> {
596 let get_routes = routes
597 .iter()
598 .filter(|route| route.method().matches(crate::HttpMethod::Get))
599 .map(RouteDefinition::path_shape)
600 .collect::<BTreeSet<_>>();
601
602 for gateway in gateways {
603 if get_routes.contains(&gateway.path_shape()) {
604 return Err(BootError::DuplicateRoute(format!("GET {}", gateway.path())));
605 }
606 }
607
608 Ok(())
609}
610
611fn validate_unique_message_patterns(patterns: &[MessagePatternDefinition]) -> Result<()> {
612 let mut seen = BTreeSet::new();
613
614 for pattern in patterns {
615 if !seen.insert(pattern.pattern()) {
616 return Err(BootError::DuplicateRoute(format!(
617 "message pattern {}",
618 pattern.pattern()
619 )));
620 }
621 }
622
623 Ok(())
624}