use std::io; use axum::{middleware, Router}; use clap::Parser; use sqlx::sqlite::SqlitePool; use tokio::net; use crate::{app::App, channel, clock, events, login, repo::pool}; pub type Result = std::result::Result; #[derive(Parser)] pub struct Args { #[arg(short, long, env, default_value = "localhost")] address: String, #[arg(short, long, env, default_value_t = 64209)] port: u16, #[arg(short, long, env, default_value = "sqlite://.hi")] database_url: String, } impl Args { pub async fn run(self) -> Result<()> { let pool = self.pool().await?; let app = App::from(pool).await?; let app = routers() .route_layer(middleware::from_fn(clock::middleware)) .with_state(app); let listener = self.listener().await?; let started_msg = started_msg(&listener)?; let serve = axum::serve(listener, app); println!("{started_msg}"); serve.await?; Ok(()) } async fn listener(&self) -> io::Result { let listen_addr = self.listen_addr(); let listener = tokio::net::TcpListener::bind(listen_addr).await?; Ok(listener) } fn listen_addr(&self) -> impl net::ToSocketAddrs + '_ { (self.address.as_str(), self.port) } async fn pool(&self) -> sqlx::Result { pool::prepare(&self.database_url).await } } fn routers() -> Router { [channel::router(), events::router(), login::router()] .into_iter() .fold(Router::default(), Router::merge) } fn started_msg(listener: &net::TcpListener) -> io::Result { let local_addr = listener.local_addr()?; Ok(format!("listening on http://{local_addr}/")) } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub enum Error { IoError(#[from] io::Error), DatabaseError(#[from] sqlx::Error), MigrateError(#[from] sqlx::migrate::MigrateError), }