//! HTTP layer: routing and OpenAPI generation (aide). //! //! Handlers stay thin: they extract the controller, call it, and map //! `ControllerError` to a status code. No business logic here. use std::sync::Arc; use aide::{ OperationInput, OperationOutput, axum::{ApiRouter, routing::get_with}, openapi::OpenApi, }; use axum::{ Extension, Json, Router, extract::FromRequestParts, http::{StatusCode, request::Parts}, }; use tracing::error; use crate::core::controller::AppController; mod docs; mod helpers; mod items; pub fn get_router(controller: AppController) -> Router { aide::generate::on_error(|err| error!("aide generated error: {err}")); aide::generate::extract_schemas(true); let mut api = OpenApi::default(); ApiRouter::new() .api_route( "/api/version", get_with( async || Json(env!("CARGO_PKG_VERSION").to_string()), |op| op.tag("misc").summary("Get app version"), ), ) .nest_api_service("/api/items", items::routes()) .nest_api_service("/api/docs", docs::routes()) .finish_api_with(&mut api, docs::api_docs_metadata) .layer(Extension(controller)) .layer(Extension(Arc::new(api))) } /// Lets a handler take an `AppController` as an argument impl FromRequestParts for AppController where S: Send + Sync, { type Rejection = StatusCode; async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { parts .extensions .get::() .cloned() .ok_or(StatusCode::INTERNAL_SERVER_ERROR) } } // The controller is not part of the request/response bodies: nothing to document impl OperationOutput for AppController { type Inner = Self; } impl OperationInput for AppController {}