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}