pub mod context;
pub mod metadata;
mod prepare;
mod roadster_app;
mod run;
#[cfg(feature = "testing")]
mod test;
pub use roadster_app::RoadsterApp;
pub use roadster_app::RoadsterAppBuilder;
pub use prepare::{PrepareOptions, PreparedApp, PreparedAppCli, PreparedAppWithoutCli, prepare};
pub use run::{run, run_prepared};
#[cfg(feature = "testing")]
pub use test::{TestAppState, run_test, run_test_with_result, test_state};
#[cfg(all(test, feature = "cli"))]
use crate::api::cli::MockTestCli;
#[cfg(feature = "cli")]
use crate::api::cli::RunCommand;
use crate::app::context::extension::ExtensionRegistry;
use crate::app::metadata::AppMetadata;
use crate::config::AppConfig;
use crate::config::environment::Environment;
#[cfg(feature = "db-sql")]
use crate::db::migration::registry::MigratorRegistry;
use crate::health::check::registry::HealthCheckRegistry;
use crate::lifecycle::registry::LifecycleHandlerRegistry;
use crate::service::registry::ServiceRegistry;
use async_trait::async_trait;
use axum_core::extract::FromRef;
use context::AppContext;
#[cfg(feature = "db-sea-orm")]
use sea_orm::ConnectOptions;
use std::future;
use std::sync::Arc;
#[cfg_attr(all(test, feature = "cli"), mockall::automock(type Error = crate::error::Error; type Cli = MockTestCli<S>;))]
#[cfg_attr(
all(test, not(feature = "cli")),
mockall::automock(type Cli = crate::util::empty::Empty;)
)]
#[async_trait]
pub trait App<S>: Send + Sync + Sized
where
S: 'static + Send + Sync + Clone,
AppContext: FromRef<S>,
{
type Error: Send + Sync + std::error::Error;
#[cfg(feature = "cli")]
type Cli: Send + Sync + clap::Args + RunCommand<Self, S>;
#[cfg(not(feature = "cli"))]
type Cli;
fn async_config_sources(
&self,
#[allow(unused_variables)] environment: &Environment,
) -> Result<Vec<Box<dyn Send + Sync + config::AsyncSource>>, Self::Error> {
Ok(vec![])
}
fn init_tracing(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<(), Self::Error>;
fn metadata(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<AppMetadata, Self::Error> {
Ok(Default::default())
}
async fn provide_context_extensions(
&self,
#[allow(unused_variables)] config: &AppConfig,
#[allow(unused_variables)] extension_registry: &mut ExtensionRegistry,
) -> Result<(), Self::Error> {
Ok(())
}
#[cfg(feature = "db-sea-orm")]
fn sea_orm_connection_options(
&self,
config: &AppConfig,
) -> Result<ConnectOptions, Self::Error> {
Ok(ConnectOptions::from(&config.database))
}
#[cfg(feature = "db-diesel-pool")]
#[allow(clippy::type_complexity)]
fn diesel_connection_customizer<C>(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<Option<Box<dyn r2d2::CustomizeConnection<C, diesel::r2d2::Error>>>, Self::Error>
where
C: 'static + diesel::connection::Connection + diesel::r2d2::R2D2Connection,
{
Ok(None)
}
#[cfg(feature = "db-diesel-postgres-pool")]
fn diesel_pg_connection_customizer(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<
Box<dyn r2d2::CustomizeConnection<crate::db::DieselPgConn, diesel::r2d2::Error>>,
Self::Error,
> {
Ok(Box::new(r2d2::NopConnectionCustomizer))
}
#[cfg(feature = "db-diesel-mysql-pool")]
fn diesel_mysql_connection_customizer(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<
Box<dyn r2d2::CustomizeConnection<crate::db::DieselMysqlConn, diesel::r2d2::Error>>,
Self::Error,
> {
Ok(Box::new(r2d2::NopConnectionCustomizer))
}
#[cfg(feature = "db-diesel-sqlite-pool")]
fn diesel_sqlite_connection_customizer(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<
Box<dyn r2d2::CustomizeConnection<crate::db::DieselSqliteConn, diesel::r2d2::Error>>,
Self::Error,
> {
Ok(Box::new(r2d2::NopConnectionCustomizer))
}
#[cfg(feature = "db-diesel-postgres-pool-async")]
fn diesel_pg_async_connection_customizer(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<
Box<
dyn bb8::CustomizeConnection<
crate::db::DieselPgConnAsync,
diesel_async::pooled_connection::PoolError,
>,
>,
Self::Error,
> {
Ok(Box::new(crate::util::empty::Empty))
}
#[cfg(feature = "db-diesel-mysql-pool-async")]
fn diesel_mysql_async_connection_customizer(
&self,
#[allow(unused_variables)] config: &AppConfig,
) -> Result<
Box<
dyn bb8::CustomizeConnection<
crate::db::DieselMysqlConnAsync,
diesel_async::pooled_connection::PoolError,
>,
>,
Self::Error,
> {
Ok(Box::new(crate::util::empty::Empty))
}
#[cfg(feature = "worker-pg")]
fn worker_pg_sqlx_pool_options(
&self,
config: &AppConfig,
) -> Result<sqlx::pool::PoolOptions<sqlx::Postgres>, Self::Error> {
if let Some(pool_config) = config
.service
.worker
.pg
.custom
.custom
.database
.as_ref()
.and_then(|config| config.pool_config.as_ref())
{
Ok(pool_config.into())
} else {
Ok((&config.database).into())
}
}
async fn provide_state(&self, context: AppContext) -> Result<S, Self::Error>;
#[cfg(feature = "db-sql")]
fn migrators(
&self,
#[allow(unused_variables)] state: &S,
#[allow(unused_variables)] registry: &mut MigratorRegistry<S>,
) -> Result<(), Self::Error> {
Ok(())
}
async fn lifecycle_handlers(
&self,
#[allow(unused_variables)] state: &S,
#[allow(unused_variables)] registry: &mut LifecycleHandlerRegistry<Self, S>,
) -> Result<(), Self::Error> {
Ok(())
}
async fn health_checks(
&self,
#[allow(unused_variables)] state: &S,
#[allow(unused_variables)] registry: &mut HealthCheckRegistry,
) -> Result<(), Self::Error> {
Ok(())
}
async fn services(
&self,
#[allow(unused_variables)] state: &S,
#[allow(unused_variables)] registry: &mut ServiceRegistry<S>,
) -> Result<(), Self::Error> {
Ok(())
}
async fn graceful_shutdown_signal(self: Arc<Self>, #[allow(unused_variables)] state: &S) {
let _output: () = future::pending().await;
}
}