mod builder;
mod config;
use std::path::Path;
use sword_core::{Config, sword_error};
pub use builder::ApplicationBuilder;
pub use config::{ApplicationConfig, ApplicationEngine};
pub struct Application {
engine: ApplicationEngine,
pub config: Config,
#[cfg(feature = "events-in-memory")]
event_shutdown_tx: Option<tokio::sync::watch::Sender<bool>>,
}
impl Application {
#[cfg(any(feature = "web", feature = "socketio", feature = "grpc"))]
pub(crate) fn new(
engine: ApplicationEngine,
config: Config,
#[cfg(feature = "events-in-memory")] event_shutdown_tx: Option<
tokio::sync::watch::Sender<bool>,
>,
) -> Self {
Self {
engine,
config,
#[cfg(feature = "events-in-memory")]
event_shutdown_tx,
}
}
pub fn builder() -> ApplicationBuilder {
ApplicationBuilder::new()
}
pub fn from_config(config: Config) -> ApplicationBuilder {
ApplicationBuilder::from_config(config)
}
pub fn from_config_path<P: AsRef<Path>>(path: P) -> ApplicationBuilder {
let config_path = path.as_ref().display().to_string();
ApplicationBuilder::from_config(
Config::builder()
.add_required_file(path.as_ref())
.build()
.unwrap_or_else(|err| {
sword_error! {
title: "Failed to load configuration from custom path",
reason: err,
context: {
"path" => config_path,
"source" => "Application::from_config_path",
},
hints: ["Ensure the file exists and contains valid TOML"],
}
}),
)
}
pub async fn run(&self) {
let app_config = self.config.get_or_default::<ApplicationConfig>();
tracing::info!(
target: "sword.startup.app",
name = app_config.name.as_deref().unwrap_or("none"),
environment = app_config.environment.as_deref().unwrap_or("none"),
graceful_shutdown = app_config.graceful_shutdown,
"Starting Sword application"
);
match &self.engine {
#[cfg(any(feature = "web", feature = "socketio"))]
ApplicationEngine::Web(app) => app.start().await,
#[cfg(feature = "grpc")]
ApplicationEngine::Grpc(app) => app.start().await,
#[allow(unreachable_patterns)]
_ => unreachable!(
"Invalid application engine configuration. Enable the appropriate feature flag to use the desired engine."
),
}
#[cfg(feature = "events-in-memory")]
if let Some(tx) = &self.event_shutdown_tx {
tracing::info!(target: "sword.events", "Signaling event subscriber shutdown");
let _ = tx.send(true);
}
}
#[allow(irrefutable_let_patterns)]
#[cfg(any(feature = "web", feature = "socketio"))]
pub fn router(&self) -> sword_web::internal::AxumRouter {
#[cfg(any(feature = "web", feature = "socketio"))]
if let ApplicationEngine::Web(app) = &self.engine {
return app.router();
}
sword_error! {
title: "Router API is only available for web based applications",
reason: "Application::router() is only valid for web/socketio applications",
context: {
"source" => "Application::router",
}
}
}
}