use std::io; use std::str::FromStr; use axum::{routing::get, Router}; use clap::Parser; use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions}; use tokio::net; 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?; sqlx::migrate!().run(&pool).await?; let app = Router::new().route("/", get(hello)).with_state(pool); 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 { let options = SqliteConnectOptions::from_str(&self.database_url)? .create_if_missing(true) .optimize_on_close(true, /* analysis_limit */ None); let pool = SqlitePoolOptions::new().connect_with(options).await?; Ok(pool) } } fn started_msg(listener: &net::TcpListener) -> io::Result { let local_addr = listener.local_addr()?; Ok(format!("listening on http://{local_addr}/")) } async fn hello() -> &'static str { "Hello, world" }