1use super::application::BootApplication;
2use super::registration::register_module;
3use crate::pipeline::PipelineComponents;
4use crate::{
5 BootError, ExceptionFilter, Guard, Interceptor, Module, ModuleRef, Pipe, Result,
6 RouteDefinition,
7};
8use std::collections::BTreeSet;
9use std::sync::Arc;
10
11#[derive(Default)]
13pub struct BootApplicationBuilder {
14 modules: Vec<Arc<dyn Module>>,
15 routes: Vec<RouteDefinition>,
16 global_pipeline: PipelineComponents,
17 global_prefix: Option<String>,
18}
19
20impl BootApplicationBuilder {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn import<M>(mut self, module: M) -> Self
27 where
28 M: Module,
29 {
30 self.modules.push(Arc::new(module));
31 self
32 }
33
34 pub fn import_arc(mut self, module: Arc<dyn Module>) -> Self {
36 self.modules.push(module);
37 self
38 }
39
40 pub fn route(mut self, route: RouteDefinition) -> Self {
42 self.routes.push(route);
43 self
44 }
45
46 pub fn global_prefix(mut self, prefix: impl Into<String>) -> Self {
48 self.global_prefix = Some(prefix.into());
49 self
50 }
51
52 pub fn use_global_pipe<P>(mut self, pipe: P) -> Self
54 where
55 P: Pipe,
56 {
57 self.global_pipeline.push_pipe(pipe);
58 self
59 }
60
61 pub fn use_global_guard<G>(mut self, guard: G) -> Self
63 where
64 G: Guard,
65 {
66 self.global_pipeline.push_guard(guard);
67 self
68 }
69
70 pub fn use_global_interceptor<I>(mut self, interceptor: I) -> Self
72 where
73 I: Interceptor,
74 {
75 self.global_pipeline.push_interceptor(interceptor);
76 self
77 }
78
79 pub fn use_global_filter<F>(mut self, filter: F) -> Self
81 where
82 F: ExceptionFilter,
83 {
84 self.global_pipeline.push_filter(filter);
85 self
86 }
87
88 pub fn build(self) -> Result<BootApplication> {
90 let module_ref = ModuleRef::new();
91 let mut seen = BTreeSet::new();
92 let mut modules = Vec::new();
93 let mut module_instances = Vec::new();
94 let mut routes = self
95 .routes
96 .into_iter()
97 .map(|route| route.with_pipeline_prefix(&self.global_pipeline))
98 .collect::<Vec<_>>();
99
100 for module in &self.modules {
101 register_module(
102 Arc::clone(module),
103 &module_ref,
104 &self.global_pipeline,
105 &mut seen,
106 &mut modules,
107 &mut module_instances,
108 &mut routes,
109 )?;
110 }
111
112 let routes = apply_global_prefix(routes, self.global_prefix.as_deref())?;
113 validate_unique_routes(&routes)?;
114
115 Ok(BootApplication {
116 routes,
117 modules,
118 module_ref,
119 module_instances,
120 })
121 }
122}
123
124fn apply_global_prefix(
125 routes: Vec<RouteDefinition>,
126 prefix: Option<&str>,
127) -> Result<Vec<RouteDefinition>> {
128 match prefix {
129 Some(prefix) => routes
130 .into_iter()
131 .map(|route| route.with_path_prefix(prefix))
132 .collect(),
133 None => Ok(routes),
134 }
135}
136
137fn validate_unique_routes(routes: &[RouteDefinition]) -> Result<()> {
138 let mut seen = BTreeSet::new();
139
140 for route in routes {
141 let key = (route.method(), route.path_shape_key());
142 if !seen.insert(key) {
143 return Err(BootError::DuplicateRoute(format!(
144 "{} {}",
145 route.method().as_str(),
146 route.path()
147 )));
148 }
149 }
150
151 Ok(())
152}