pub mod error_handler;
pub mod provider;
use std::sync::Arc;
use axum::routing::Router;
use crate::container::Container;
use crate::error::{RokError, RokException};
use self::provider::ServiceProvider;
pub struct App {
pub container: Container,
providers: Vec<Box<dyn ServiceProvider>>,
middleware: Vec<Box<dyn FnOnce(Router) -> Router + Send>>,
error_handler: Option<Arc<dyn Fn(Box<dyn RokException>) -> axum::response::Response + Send + Sync>>,
router_fn: Option<Box<dyn FnOnce(&App) -> Router + Send>>,
extra_routers: Vec<Router>,
}
impl App {
pub fn boot() -> Self {
let _ = dotenvy::dotenv();
#[cfg(feature = "config")]
{
let discovered = crate::config::discover_configs();
if !discovered.is_empty() {
tracing::info!("Discovered {} config file(s)", discovered.len());
for (key, _path) in &discovered {
tracing::debug!(" config/{}.toml", key);
}
}
}
Self {
container: Container::new(),
providers: Vec::new(),
middleware: Vec::new(),
error_handler: None,
router_fn: None,
extra_routers: Vec::new(),
}
}
pub fn register<P: ServiceProvider + 'static>(mut self, provider: P) -> Self {
self.providers.push(Box::new(provider));
self
}
pub fn middleware<F>(mut self, f: F) -> Self
where
F: FnOnce(Router) -> Router + Send + 'static,
{
self.middleware.push(Box::new(f));
self
}
pub fn with_error_handler<F>(mut self, handler: F) -> Self
where
F: Fn(Box<dyn RokException>) -> axum::response::Response + Send + Sync + 'static,
{
self.error_handler = Some(Arc::new(handler));
self
}
pub fn routes(mut self, f: fn(&App) -> Router) -> Self {
self.router_fn = Some(Box::new(f));
self
}
pub fn merge(mut self, router: Router) -> Self {
self.extra_routers.push(router);
self
}
pub fn resolve<T>(&self) -> Result<Arc<T>, crate::container::ContainerError>
where
T: Send + Sync + 'static,
{
self.container.make::<T>()
}
pub fn config<T: crate::config::Configurable + crate::config::FromEnv>() -> T {
crate::config::load_config::<T>()
.unwrap_or_else(|| crate::config::Config::load::<T>())
}
pub async fn into_router(mut self) -> Result<Router, RokError> {
let providers = std::mem::take(&mut self.providers);
for provider in &providers {
let mut app_ref = AppRef(&mut self);
provider
.register(&mut app_ref)
.await
.map_err(|e| RokError::Internal(format!("{}::register: {e}", provider.name())))?;
}
for provider in &providers {
provider
.boot(&self)
.await
.map_err(|e| RokError::Internal(format!("{}::boot: {e}", provider.name())))?;
}
let mut router = match self.router_fn.take() {
Some(f) => f(&self),
None => Router::new(),
};
for extra in self.extra_routers {
router = router.merge(extra);
}
let middleware = std::mem::take(&mut self.middleware);
Ok(middleware.into_iter().rev().fold(router, |r, f| f(r)))
}
pub async fn serve(self, addr: &str) -> Result<(), RokError> {
let router = self.into_router().await?;
let listener = tokio::net::TcpListener::bind(addr)
.await
.map_err(|e| RokError::Internal(format!("bind {addr}: {e}")))?;
axum::serve(listener, router)
.await
.map_err(|e| RokError::Internal(format!("server: {e}")))?;
Ok(())
}
}
pub struct AppRef<'a>(pub &'a mut App);
impl<'a> AppRef<'a> {
pub fn resolve<T>(&self) -> Result<Arc<T>, crate::container::ContainerError>
where
T: Send + Sync + 'static,
{
self.0.container.make::<T>()
}
pub fn singleton<T>(&mut self, instance: T)
where
T: Send + Sync + 'static,
{
self.0.container.singleton(instance);
}
pub fn bind<T, F>(&mut self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
self.0.container.bind::<T, F>(factory);
}
}
pub use self::error_handler::CatchLayer;