pub mod routes; use anyhow::Result; use axum::extract::FromRef; use axum_extra::extract::cookie::Key; use pin_project::pin_project; use std::future::{Future, IntoFuture}; use std::net::SocketAddr; use std::pin::pin; use std::pin::Pin; use tracing::info; #[pin_project] pub struct ZeroToAxum { #[pin] server: Pin> + Send>>, bound_addr: SocketAddr, } impl Future for ZeroToAxum { type Output = Result<(), std::io::Error>; fn poll( self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { self.project().server.poll(cx) } } impl ZeroToAxum { pub fn local_addr(&self) -> SocketAddr { self.bound_addr } pub async fn serve(addr: SocketAddr) -> ZeroToAxum { let state = AppState { // TODO: pull from config key: Key::generate(), }; let app = routes::build().with_state(state); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); let bound_addr = listener.local_addr().unwrap(); let server = axum::serve(listener, app); info!("server started, listening on {addr:?}"); ZeroToAxum { server: Box::pin(server.into_future()), bound_addr, } } } #[derive(Clone)] pub struct AppState { // The key used to encrypt cookies. key: Key, } impl FromRef for Key { fn from_ref(state: &AppState) -> Self { state.key.clone() } }