use crate::adapters::AdapterRegistry;
use crate::application::Application;
use crate::interceptor::InterceptorRegistrar;
use crate::module::Module;
use crate::runtimes::http::HttpRuntime;
use axum::{
extract::Request as AxumRequest, response::IntoResponse, routing::Route,
};
use std::convert::Infallible;
use sword_core::layers::LayerStack;
use sword_core::{Config, ConfigRegistrar, DependencyContainer, Provider, State};
use tower::{Layer, Service};
pub struct ApplicationBuilder {
state: State,
container: DependencyContainer,
adapter_registry: AdapterRegistry,
layer_stack: LayerStack<State>,
pub config: Config,
}
impl ApplicationBuilder {
pub fn new() -> Self {
Self::from_config(Config::new().expect("Configuration loading error"))
}
pub fn from_config(config: Config) -> Self {
let state = State::new();
state.insert(config.clone());
for ConfigRegistrar { register } in inventory::iter::<ConfigRegistrar> {
register(&state, &config)
}
Self {
state,
config,
container: DependencyContainer::new(),
adapter_registry: AdapterRegistry::new(),
layer_stack: LayerStack::new(),
}
}
pub fn with_module<M>(self) -> Self
where
M: Module,
{
futures_lite::future::block_on(M::register_providers(
&self.config,
self.container.provider_registry(),
));
M::register_components(self.container.component_registry());
M::register_adapters(&self.adapter_registry);
self
}
pub fn with_layer<L>(mut self, layer: L) -> Self
where
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<AxumRequest> + Clone + Send + Sync + 'static,
<L::Service as Service<AxumRequest>>::Response: IntoResponse + 'static,
<L::Service as Service<AxumRequest>>::Error: Into<Infallible> + 'static,
<L::Service as Service<AxumRequest>>::Future: Send + 'static,
{
self.layer_stack.push(layer);
self
}
pub fn with_provider<T>(self, provider: T) -> Self
where
T: Provider + 'static,
{
self.container.provider_registry().register(provider);
self
}
pub fn build(mut self) -> Application {
self.container
.build_all(&self.state)
.unwrap_or_else(|err| {
eprintln!("\n[!] Failed to build dependency injection container\n");
eprintln!(" Error: {}\n", err);
eprintln!(" Check that all required components and providers are registered correctly.\n");
panic!("DI container build failed");
});
for InterceptorRegistrar { register } in
inventory::iter::<InterceptorRegistrar>
{
register(&self.state);
}
let layer_stack = std::mem::take(&mut self.layer_stack);
let http_runtime = HttpRuntime::new(
self.state.clone(),
self.config.clone(),
layer_stack,
&self.adapter_registry,
);
Application::new(http_runtime, self.config)
}
}
impl Default for ApplicationBuilder {
fn default() -> Self {
Self::new()
}
}