1pub mod context;
2pub mod metadata;
3mod prepare;
4mod roadster_app;
5mod run;
6#[cfg(feature = "testing")]
7mod test;
8
9pub use roadster_app::RoadsterApp;
16
17pub use roadster_app::RoadsterAppBuilder;
24
25pub use prepare::{PrepareOptions, PreparedApp, PreparedAppCli, PreparedAppWithoutCli, prepare};
26pub use run::{run, run_prepared};
27#[cfg(feature = "testing")]
28pub use test::{TestAppState, run_test, run_test_with_result, test_state};
29
30#[cfg(all(test, feature = "cli"))]
31use crate::api::cli::MockTestCli;
32#[cfg(feature = "cli")]
33use crate::api::cli::RunCommand;
34use crate::app::context::extension::ExtensionRegistry;
35use crate::app::metadata::AppMetadata;
36use crate::config::AppConfig;
37use crate::config::environment::Environment;
38#[cfg(feature = "db-sql")]
39use crate::db::migration::Migrator;
40use crate::error::RoadsterResult;
41use crate::health::check::registry::HealthCheckRegistry;
42use crate::lifecycle::registry::LifecycleHandlerRegistry;
43use crate::service::registry::ServiceRegistry;
44use crate::tracing::init_tracing;
45use async_trait::async_trait;
46use axum_core::extract::FromRef;
47use context::AppContext;
48#[cfg(feature = "db-sea-orm")]
49use sea_orm::ConnectOptions;
50use std::future;
51use std::sync::Arc;
52
53#[cfg_attr(all(test, feature = "cli"), mockall::automock(type Cli = MockTestCli<S>;))]
54#[cfg_attr(
55 all(test, not(feature = "cli")),
56 mockall::automock(type Cli = crate::util::empty::Empty;)
57)]
58#[async_trait]
59pub trait App<S>: Send + Sync + Sized
60where
61 S: Clone + Send + Sync + 'static,
62 AppContext: FromRef<S>,
63{
64 #[cfg(feature = "cli")]
65 type Cli: clap::Args + RunCommand<Self, S> + Send + Sync;
66 #[cfg(not(feature = "cli"))]
67 type Cli;
68
69 fn async_config_sources(
70 &self,
71 _environment: &Environment,
72 ) -> RoadsterResult<Vec<Box<dyn config::AsyncSource + Send + Sync>>> {
73 Ok(vec![])
74 }
75
76 fn init_tracing(&self, config: &AppConfig) -> RoadsterResult<()> {
77 init_tracing(config, &self.metadata(config)?)?;
78
79 Ok(())
80 }
81
82 fn metadata(&self, _config: &AppConfig) -> RoadsterResult<AppMetadata> {
83 Ok(Default::default())
84 }
85
86 async fn provide_context_extensions(
87 &self,
88 _config: &AppConfig,
89 _extension_registry: &mut ExtensionRegistry,
90 ) -> RoadsterResult<()> {
91 Ok(())
92 }
93
94 #[cfg(feature = "db-sea-orm")]
95 fn sea_orm_connection_options(&self, config: &AppConfig) -> RoadsterResult<ConnectOptions> {
96 Ok(ConnectOptions::from(&config.database))
97 }
98
99 #[cfg(feature = "db-diesel-pool")]
100 fn diesel_connection_customizer<C>(
101 &self,
102 _config: &AppConfig,
103 ) -> RoadsterResult<Option<Box<dyn r2d2::CustomizeConnection<C, diesel::r2d2::Error>>>>
104 where
105 C: 'static + diesel::connection::Connection + diesel::r2d2::R2D2Connection,
106 {
107 Ok(None)
108 }
109
110 #[cfg(feature = "db-diesel-postgres-pool")]
111 fn diesel_pg_connection_customizer(
112 &self,
113 _config: &AppConfig,
114 ) -> RoadsterResult<
115 Box<dyn r2d2::CustomizeConnection<crate::db::DieselPgConn, diesel::r2d2::Error>>,
116 > {
117 Ok(Box::new(r2d2::NopConnectionCustomizer))
118 }
119
120 #[cfg(feature = "db-diesel-mysql-pool")]
121 fn diesel_mysql_connection_customizer(
122 &self,
123 _config: &AppConfig,
124 ) -> RoadsterResult<
125 Box<dyn r2d2::CustomizeConnection<crate::db::DieselMysqlConn, diesel::r2d2::Error>>,
126 > {
127 Ok(Box::new(r2d2::NopConnectionCustomizer))
128 }
129
130 #[cfg(feature = "db-diesel-sqlite-pool")]
131 fn diesel_sqlite_connection_customizer(
132 &self,
133 _config: &AppConfig,
134 ) -> RoadsterResult<
135 Box<dyn r2d2::CustomizeConnection<crate::db::DieselSqliteConn, diesel::r2d2::Error>>,
136 > {
137 Ok(Box::new(r2d2::NopConnectionCustomizer))
138 }
139
140 #[cfg(feature = "db-diesel-postgres-pool-async")]
141 fn diesel_pg_async_connection_customizer(
142 &self,
143 _config: &AppConfig,
144 ) -> RoadsterResult<
145 Box<
146 dyn bb8::CustomizeConnection<
147 crate::db::DieselPgConnAsync,
148 diesel_async::pooled_connection::PoolError,
149 >,
150 >,
151 > {
152 Ok(Box::new(crate::util::empty::Empty))
153 }
154
155 #[cfg(feature = "db-diesel-mysql-pool-async")]
156 fn diesel_mysql_async_connection_customizer(
157 &self,
158 _config: &AppConfig,
159 ) -> RoadsterResult<
160 Box<
161 dyn bb8::CustomizeConnection<
162 crate::db::DieselMysqlConnAsync,
163 diesel_async::pooled_connection::PoolError,
164 >,
165 >,
166 > {
167 Ok(Box::new(crate::util::empty::Empty))
168 }
169
170 #[cfg(feature = "worker-pg")]
172 fn worker_pg_sqlx_pool_options(
173 &self,
174 config: &AppConfig,
175 ) -> RoadsterResult<sqlx::pool::PoolOptions<sqlx::Postgres>> {
176 if let Some(pool_config) = config
177 .service
178 .worker
179 .pg
180 .custom
181 .custom
182 .database
183 .as_ref()
184 .and_then(|config| config.pool_config.as_ref())
185 {
186 Ok(pool_config.into())
187 } else {
188 Ok((&config.database).into())
189 }
190 }
191
192 async fn provide_state(&self, context: AppContext) -> RoadsterResult<S>;
198
199 #[cfg(feature = "db-sql")]
203 fn migrators(&self, _state: &S) -> RoadsterResult<Vec<Box<dyn Migrator<S>>>> {
204 Ok(Default::default())
205 }
206
207 async fn lifecycle_handlers(
208 &self,
209 _registry: &mut LifecycleHandlerRegistry<Self, S>,
210 _state: &S,
211 ) -> RoadsterResult<()> {
212 Ok(())
213 }
214
215 async fn health_checks(
217 &self,
218 _registry: &mut HealthCheckRegistry,
219 _state: &S,
220 ) -> RoadsterResult<()> {
221 Ok(())
222 }
223
224 async fn services(&self, _registry: &mut ServiceRegistry<S>, _state: &S) -> RoadsterResult<()> {
226 Ok(())
227 }
228
229 async fn graceful_shutdown_signal(self: Arc<Self>, _state: &S) {
233 let _output: () = future::pending().await;
234 }
235}