//! Mizan axum HTTP adapter — typed RPC over `mizan-core`'s function registry. //! //! Usage: //! ```ignore //! use axum::Router; //! use mizan_axum::router; //! //! #[tokio::main] //! async fn main() { //! let app = Router::new().nest("/api/mizan", router()); //! let listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await.unwrap(); //! axum::serve(listener, app).await.unwrap(); //! } //! ``` //! //! Exposed endpoints (mirroring `mizan-fastapi` / `mizan-django`): //! * `GET /session/` — session-init probe (placeholder CSRF token) //! * `POST /call/` — RPC dispatch with invalidate+merge response //! * `GET /ctx/:name/` — bundled context fetch mod errors; mod handlers; pub use errors::ApiError; pub use handlers::{ context_fetch, function_call, session_init, AppStateAny, CallBody, CallResponse, }; use axum::routing::{get, post}; use axum::Router; use std::any::Any; use std::sync::Arc; /// Build the Mizan router with user-supplied app state. The state is /// type-erased into an `Arc` and threaded into every /// dispatch via `RequestHandle`. Handlers downcast to their concrete state /// type. /// /// Mount under a prefix: /// `Router::new().nest("/api/mizan", router(my_state))`. pub fn router(state: S) -> Router where S: Any + Send + Sync + 'static, { let state: AppStateAny = Arc::new(state); Router::new() .route("/session/", get(handlers::session_init)) .route("/call/", post(handlers::function_call)) .route("/ctx/:context_name/", get(handlers::context_fetch)) .with_state(state) } /// Router variant for callers that have no app state to thread — the /// dispatch path receives a unit-typed handle. Used by the AFI fixture /// and other stateless test apps. pub fn router_stateless() -> Router { router(()) }