use std::net::SocketAddr;
use axum::Router;
use axum::extract::{Path, State};
use axum::routing::get;
use modrun::logging::init;
use modrun::{Error, Lifecycle, Modrun, Module, task_with};
use tokio::net::TcpListener;
#[derive(Clone)]
struct Config {
addr: SocketAddr,
}
#[derive(Clone)]
struct Greeter {
prefix: String,
}
#[derive(Clone)]
struct AppState {
greeter: Greeter,
}
fn new_greeter() -> Greeter {
Greeter {
prefix: "hello".into(),
}
}
fn new_state(greeter: Greeter) -> AppState {
AppState { greeter }
}
async fn index(State(state): State<AppState>) -> String {
format!("{} from modrun + axum\n", state.greeter.prefix)
}
async fn hello(State(state): State<AppState>, Path(name): Path<String>) -> String {
format!("{}, {name}!\n", state.greeter.prefix)
}
fn register_http(lc: Lifecycle, cfg: Config, state: AppState) -> modrun::Result<()> {
let addr = cfg.addr;
lc.append(task_with(
"http.serve",
move || async move {
let listener = TcpListener::bind(addr)
.await
.map_err(|e| Error::io(format!("bind {addr}"), e))?;
println!("listening on http://{addr}");
Ok(listener)
},
move |listener, stopped| async move {
let app = Router::new()
.route("/", get(index))
.route("/hello/{name}", get(hello))
.with_state(state);
axum::serve(listener, app)
.with_graceful_shutdown(stopped)
.await
.map_err(|e| Error::io(format!("serve {addr}"), e))
},
))
}
fn http_domain() -> Module {
Module::builder("http")
.provide_private(new_greeter)
.provide(new_state)
.invoke(register_http)
}
#[tokio::main]
async fn main() -> modrun::Result<()> {
init();
Modrun::builder()
.supply(Config {
addr: SocketAddr::from(([127, 0, 0, 1], 3000)),
})
.module(http_domain())
.run()
.await
}