use super::application::BootApplication;
use super::registration::register_module;
use crate::pipeline::PipelineComponents;
use crate::{
BootError, ExceptionFilter, Guard, Interceptor, Module, ModuleRef, Pipe, Result,
RouteDefinition,
};
use std::collections::BTreeSet;
use std::sync::Arc;
#[derive(Default)]
pub struct BootApplicationBuilder {
modules: Vec<Arc<dyn Module>>,
routes: Vec<RouteDefinition>,
global_pipeline: PipelineComponents,
global_prefix: Option<String>,
}
impl BootApplicationBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn import<M>(mut self, module: M) -> Self
where
M: Module,
{
self.modules.push(Arc::new(module));
self
}
pub fn import_arc(mut self, module: Arc<dyn Module>) -> Self {
self.modules.push(module);
self
}
pub fn route(mut self, route: RouteDefinition) -> Self {
self.routes.push(route);
self
}
pub fn global_prefix(mut self, prefix: impl Into<String>) -> Self {
self.global_prefix = Some(prefix.into());
self
}
pub fn use_global_pipe<P>(mut self, pipe: P) -> Self
where
P: Pipe,
{
self.global_pipeline.push_pipe(pipe);
self
}
pub fn use_global_guard<G>(mut self, guard: G) -> Self
where
G: Guard,
{
self.global_pipeline.push_guard(guard);
self
}
pub fn use_global_interceptor<I>(mut self, interceptor: I) -> Self
where
I: Interceptor,
{
self.global_pipeline.push_interceptor(interceptor);
self
}
pub fn use_global_filter<F>(mut self, filter: F) -> Self
where
F: ExceptionFilter,
{
self.global_pipeline.push_filter(filter);
self
}
pub fn build(self) -> Result<BootApplication> {
let module_ref = ModuleRef::new();
let mut seen = BTreeSet::new();
let mut modules = Vec::new();
let mut module_instances = Vec::new();
let mut routes = self
.routes
.into_iter()
.map(|route| route.with_pipeline_prefix(&self.global_pipeline))
.collect::<Vec<_>>();
for module in &self.modules {
register_module(
Arc::clone(module),
&module_ref,
&self.global_pipeline,
&mut seen,
&mut modules,
&mut module_instances,
&mut routes,
)?;
}
let routes = apply_global_prefix(routes, self.global_prefix.as_deref())?;
validate_unique_routes(&routes)?;
Ok(BootApplication {
routes,
modules,
module_ref,
module_instances,
})
}
}
fn apply_global_prefix(
routes: Vec<RouteDefinition>,
prefix: Option<&str>,
) -> Result<Vec<RouteDefinition>> {
match prefix {
Some(prefix) => routes
.into_iter()
.map(|route| route.with_path_prefix(prefix))
.collect(),
None => Ok(routes),
}
}
fn validate_unique_routes(routes: &[RouteDefinition]) -> Result<()> {
let mut seen = BTreeSet::new();
for route in routes {
let key = (route.method(), route.path_shape_key());
if !seen.insert(key) {
return Err(BootError::DuplicateRoute(format!(
"{} {}",
route.method().as_str(),
route.path()
)));
}
}
Ok(())
}