use { tower_http::{services::ServeDir, trace::TraceLayer}, tracing::Level, }; mod app; mod authentication; mod config; mod db; mod error; use config::get_config; use db::{Database, PostgresDatabase}; /// An unrecoverable error which requires the server to shut down #[derive(Debug)] struct FatalError { message: &'static str, inner: Option>, } impl std::fmt::Display for FatalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.inner { Some(inner_err) => write!(f, "{}: {}", self.message, inner_err), None => write!(f, "{}", self.message), } } } impl std::error::Error for FatalError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.inner.as_deref() } } impl From for FatalError { fn from(value: config::Error) -> Self { FatalError { message: "Loading config", inner: Some(Box::new(value)), } } } impl From for FatalError { fn from(value: tracing::subscriber::SetGlobalDefaultError) -> Self { FatalError { message: "Loading config", inner: Some(Box::new(value)), } } } impl From for FatalError { fn from(value: db::InitialisationError) -> Self { FatalError { message: "initialising database connection", inner: Some(Box::new(value)), } } } impl From for FatalError { fn from(value: std::io::Error) -> Self { FatalError { message: "Initialising", inner: Some(Box::new(value)), } } } fn main() { let runtime = tokio::runtime::Runtime::new().unwrap(); std::process::exit(match runtime.block_on(locality_main()) { Ok(()) => 0, Err(err) => { eprintln!("ERROR: {}", err); 1 } }) } async fn locality_main() -> Result<(), FatalError> { let config = get_config()?; let subscriber = tracing_subscriber::FmtSubscriber::builder() .pretty() .with_max_level(Level::DEBUG) .finish(); tracing::subscriber::set_global_default(subscriber)?; let db_pool = PostgresDatabase::new(&config.database_url)?; db_pool.migrate_to_current_version().await.unwrap(); let app = app::routes() .with_state(app::AppState { db: db_pool }) .nest_service("/static", ServeDir::new(&config.static_file_path)) .layer(TraceLayer::new_for_http()); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; axum::serve(listener, app).await?; Ok(()) }