use axum::extract::State; use axum::response::IntoResponse; use axum::routing::get; use axum::Router; use hemx_axum::{runtime_js, runtime_js_path, EffectResponse, InteractionRequest}; use hemx_workout_example::{self as workout_app, AppState}; use std::net::SocketAddr; use std::str::FromStr; #[tokio::main] async fn main() { let app = app(AppState::demo()); let addr = std::env::var("HEMX_WORKOUT_ADDR") .ok() .and_then(|value| SocketAddr::from_str(&value).ok()) .unwrap_or_else(|| SocketAddr::from(([127, 0, 0, 1], 3028))); let listener = tokio::net::TcpListener::bind(addr).await.expect( "bind workout example address; set HEMX_WORKOUT_ADDR=127.0.0.1:3030 if the default is busy", ); eprintln!("hemx workout example: http://{addr}"); axum::serve(listener, app).await.unwrap(); } pub fn app(state: AppState) -> Router { Router::new() .route("/", get(page).post(interact)) .route("/workout.css", get(workout_css)) .route(runtime_js_path(), get(runtime)) .with_state(state) } async fn page(State(state): State) -> impl IntoResponse { axum::response::Html( state.with_workout(|workout| workout_app::page(runtime_js_path(), workout).into_string()), ) } async fn interact( State(state): State, request: InteractionRequest, ) -> Result { request.dispatch(workout_app::interactions(state)) } async fn workout_css() -> impl IntoResponse { ( [("content-type", "text/css; charset=utf-8")], include_str!("../templates/workout.css"), ) } async fn runtime() -> impl IntoResponse { runtime_js() } #[cfg(test)] mod tests { use super::*; #[test] fn routes_can_be_built_for_run_and_deploy_smoke() { let _app = app(AppState::demo()); } }