Skip to main content

by_loco_openapi/
openapi.rs

1use loco_rs::app::AppContext;
2use std::sync::{Mutex, OnceLock};
3use utoipa_axum::router::{OpenApiRouter, UtoipaMethodRouter};
4
5static OPENAPI_ROUTES: OnceLock<Mutex<Vec<OpenApiRouter<AppContext>>>> = OnceLock::new();
6
7fn get_routes() -> &'static Mutex<Vec<OpenApiRouter<AppContext>>> {
8    OPENAPI_ROUTES.get_or_init(|| Mutex::new(Vec::new()))
9}
10
11// Register a route for later merging
12pub fn add_route(route: OpenApiRouter<AppContext>) {
13    if let Ok(mut routes) = get_routes().lock() {
14        routes.push(route);
15    }
16}
17
18// Clears all registered routes in the `OPENAPI_ROUTES`
19// Mostly used for testing, to prevent routes added from different test runs from overlapping
20pub fn clear_routes() {
21    if let Ok(mut routes) = get_routes().lock() {
22        routes.clear();
23    }
24}
25
26// Get a merged router containing all collected routes
27#[must_use]
28pub fn get_merged_router() -> OpenApiRouter<AppContext> {
29    let mut result = OpenApiRouter::new();
30
31    if let Ok(routes) = get_routes().lock() {
32        for route in routes.iter() {
33            result = result.merge(route.clone());
34        }
35    }
36    result
37}
38
39/// Auto collect the openapi routes
40/// ```rust
41/// # use axum::debug_handler;
42/// use loco_openapi::prelude::*;
43/// # use loco_rs::prelude::*;
44/// # use serde::Serialize;
45/// # #[derive(Serialize, Debug, ToSchema)]
46/// # pub struct Album {
47/// #     title: String,
48/// #     rating: u32,
49/// # }
50/// # #[utoipa::path(
51/// #     get,
52/// #     path = "/api/album/get_album",
53/// #     tags = ["album"],
54/// #     responses(
55/// #         (status = 200, description = "Album found", body = Album),
56/// #     ),
57/// # )]
58/// # #[debug_handler]
59/// # pub async fn get_album(State(_ctx): State<AppContext>) -> Result<Response> {
60/// #     format::json(Album {
61/// #         title: "VH II".to_string(),
62/// #         rating: 10,
63/// #     })
64/// # }
65///
66/// // Swap from:
67///  Routes::new()
68///     .add("/album", get(get_album));
69/// // To:
70/// Routes::new()
71///     .add("/get_album", openapi(get(get_album), routes!(get_album)));
72/// ```
73pub fn openapi(
74    method: axum::routing::MethodRouter<AppContext>,
75    method_openapi: UtoipaMethodRouter<AppContext>,
76) -> axum::routing::MethodRouter<AppContext> {
77    let router = OpenApiRouter::new().routes(method_openapi);
78    add_route(router);
79    method
80}