use crate::application::error::{EngineError, StartupError};
use crate::application::resources::Resources;
use crate::application::ty::Application;
#[cfg(feature = "jobs")]
use crate::application::resources::WorkerHandle;
pub(crate) struct Started {
pub(crate) resources: Resources,
#[cfg(feature = "jobs")]
pub(crate) worker: Option<WorkerHandle>,
}
pub(crate) async fn startup<S>(
app: Application<S>,
) -> Result<(Started, Application<S>), EngineError>
where
S: Clone + Send + Sync + 'static,
{
let Application {
routes,
proxy,
bind_address,
port,
#[cfg(feature = "inertia")]
inertia_config,
#[cfg(feature = "inertia")]
page_contracts,
#[cfg(feature = "pages")]
pages,
#[cfg(feature = "pages")]
maintenance_guard,
#[cfg(feature = "db")]
database,
#[cfg(feature = "cache")]
cache_config,
#[cfg(feature = "storage")]
storage_config,
#[cfg(feature = "mail")]
mail_config,
#[cfg(feature = "jobs")]
jobs_registry,
#[cfg(feature = "jobs")]
worker_config,
#[cfg(feature = "dx")]
error_mapping,
#[cfg(feature = "dev-proxy")]
dev_proxy_endpoint,
} = app;
let resolved = Application {
routes,
proxy,
bind_address,
port,
#[cfg(feature = "inertia")]
inertia_config,
#[cfg(feature = "inertia")]
page_contracts,
#[cfg(feature = "pages")]
pages,
#[cfg(feature = "pages")]
maintenance_guard,
#[cfg(feature = "db")]
database: None,
#[cfg(feature = "cache")]
cache_config: None,
#[cfg(feature = "storage")]
storage_config: None,
#[cfg(feature = "mail")]
mail_config: None,
#[cfg(feature = "jobs")]
jobs_registry: None,
#[cfg(feature = "jobs")]
worker_config: None,
#[cfg(feature = "dx")]
error_mapping,
#[cfg(feature = "dev-proxy")]
dev_proxy_endpoint,
};
#[cfg(feature = "db")]
let mut db_handle: Option<crate::db::Db> = None;
#[cfg(feature = "jobs")]
let mut jobs_handle: Option<arcature_jobs::Jobs> = None;
#[cfg(feature = "jobs")]
let mut worker_handle: Option<WorkerHandle> = None;
#[cfg(feature = "cache")]
let mut cache_handle: Option<crate::cache::Cache> = None;
#[cfg(feature = "storage")]
let mut storage_handle: Option<crate::storage::Storage> = None;
#[cfg(feature = "mail")]
let mut mail_handle: Option<crate::mail::Mailer> = None;
macro_rules! fail {
($err:expr) => {{
let partial = Started {
resources: Resources {
#[cfg(feature = "db")]
db: db_handle.take(),
#[cfg(feature = "cache")]
cache: cache_handle.take(),
#[cfg(feature = "storage")]
storage: storage_handle.take(),
#[cfg(feature = "mail")]
mail: mail_handle.take(),
#[cfg(feature = "jobs")]
jobs: jobs_handle.take(),
},
#[cfg(feature = "jobs")]
worker: worker_handle.take(),
};
let _ = super::shutdown::shutdown(partial).await;
return Err($err);
}};
}
#[cfg(feature = "db")]
if let Some(config) = database {
match crate::db::Db::connect(config).await {
Ok(db) => db_handle = Some(db),
Err(source) => fail!(EngineError::Startup {
subsystem: "db",
stage: "connect",
source: StartupError::Db(source),
}),
}
}
#[cfg(feature = "jobs")]
if let Some(jobs_registry) = jobs_registry {
let pool = match db_handle.as_ref().map(|db| db.sqlx().clone()) {
Some(pool) => pool,
None => fail!(EngineError::Startup {
subsystem: "jobs",
stage: "pool",
source: StartupError::JobsMigrate(arcature_jobs::MigrateError::Database {
source: arcature_db::sqlx::Error::PoolClosed,
})
}),
};
let jobs = arcature_jobs::Jobs::new(pool.clone());
if let Err(source) = jobs.migrate().await {
fail!(EngineError::Startup {
subsystem: "jobs",
stage: "migrate",
source: StartupError::JobsMigrate(source),
});
}
let db = db_handle
.as_ref()
.expect("db configured before the jobs worker (jobs requires db)");
let registry = jobs_registry.resolve(db);
let worker_config = worker_config.unwrap_or_default();
let worker = arcature_jobs::Worker::builder(pool, registry)
.config(worker_config)
.build();
let shutdown = tokio_util::sync::CancellationToken::new();
let worker_shutdown = shutdown.clone();
let join = tokio::spawn(async move { worker.run(worker_shutdown).await });
jobs_handle = Some(jobs);
worker_handle = Some(WorkerHandle { join, shutdown });
}
#[cfg(feature = "cache")]
if let Some(config) = cache_config {
match crate::cache::Cache::connect(config).await {
Ok(cache) => cache_handle = Some(cache),
Err(source) => fail!(EngineError::Startup {
subsystem: "cache",
stage: "connect",
source: StartupError::Cache(source),
}),
}
}
#[cfg(feature = "storage")]
if let Some(config) = storage_config {
match crate::storage::Storage::connect(config).await {
Ok(storage) => storage_handle = Some(storage),
Err(source) => fail!(EngineError::Startup {
subsystem: "storage",
stage: "connect",
source: StartupError::Storage(source),
}),
}
}
#[cfg(feature = "mail")]
if let Some(config) = mail_config {
match crate::mail::Mailer::smtp(config) {
Ok(mailer) => mail_handle = Some(mailer),
Err(source) => fail!(EngineError::Startup {
subsystem: "mail",
stage: "connect",
source: StartupError::Mail(source),
}),
}
}
Ok((
Started {
resources: Resources {
#[cfg(feature = "db")]
db: db_handle,
#[cfg(feature = "cache")]
cache: cache_handle,
#[cfg(feature = "storage")]
storage: storage_handle,
#[cfg(feature = "mail")]
mail: mail_handle,
#[cfg(feature = "jobs")]
jobs: jobs_handle,
},
#[cfg(feature = "jobs")]
worker: worker_handle,
},
resolved,
))
}