#![doc = include_str!("../README.md")]
use std::{
ops::Deref,
sync::{Mutex, MutexGuard},
};
use axum::Router;
use once_cell::sync::Lazy;
use serde::Serialize;
use utoipa::openapi::Paths;
#[cfg(feature = "derive")]
extern crate axum_openapi3_derive;
#[cfg(feature = "derive")]
pub use axum_openapi3_derive::endpoint;
pub use utoipa;
pub static ENDPOINTS: std::sync::Mutex<Vec<utoipa::openapi::Paths>> = std::sync::Mutex::new(vec![]);
pub trait AddRoute<S> {
fn add(
self,
r: (
&str,
axum::routing::MethodRouter<S, std::convert::Infallible>,
),
) -> Self;
}
impl<S: std::clone::Clone + std::marker::Send + std::marker::Sync + 'static> AddRoute<S>
for Router<S>
{
fn add(
self,
r: (
&str,
axum::routing::MethodRouter<S, std::convert::Infallible>,
),
) -> Self {
self.route(r.0, r.1)
}
}
static OPENAPI_BUILT: Lazy<Mutex<Option<utoipa::openapi::OpenApi>>> =
Lazy::new(|| Mutex::new(None));
pub fn reset_openapi() {
let mut endpoints = ENDPOINTS.lock().unwrap();
*endpoints = vec![];
*OPENAPI_BUILT.lock().unwrap() = None;
}
pub struct OpenApiWrapper<'a> {
guard: MutexGuard<'a, Option<utoipa::openapi::OpenApi>>,
}
impl Deref for OpenApiWrapper<'_> {
type Target = utoipa::openapi::OpenApi;
fn deref(&self) -> &Self::Target {
self.guard.as_ref().unwrap()
}
}
impl Serialize for OpenApiWrapper<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.guard.as_ref().unwrap().serialize(serializer)
}
}
pub fn build_openapi<'openapi, F>(f: F) -> OpenApiWrapper<'openapi>
where
F: Fn() -> utoipa::openapi::OpenApiBuilder,
{
let mut openapi = OPENAPI_BUILT.lock().unwrap();
if openapi.is_none() {
let mut endpoints = ENDPOINTS.lock().unwrap();
let paths = endpoints.drain(..).fold(Paths::default(), |mut acc, x| {
acc.merge(x);
acc
});
let openapi_builder = f().paths(paths);
*openapi = Some(openapi_builder.build());
}
OpenApiWrapper { guard: openapi }
}