okapi_operation/axum_integration/
mod.rs1#![doc = include_str!("../../docs/axum_integration.md")]
2
3#[doc(hidden)]
4pub use paste::paste;
5
6pub use self::{
7 handler_traits::{HandlerExt, HandlerWithOperation, ServiceExt, ServiceWithOperation},
8 method_router::*,
9 router::{DEFAULT_OPENAPI_PATH, Router},
10};
11
12#[cfg(feature = "yaml")]
13mod yaml;
14
15mod handler_traits;
16mod method_router;
17mod operations;
18mod router;
19mod trait_impls;
20mod utils;
21
22use axum::{
23 Json,
24 extract::State,
25 response::{IntoResponse, Response},
26};
27use http::{
28 HeaderMap, HeaderValue, StatusCode,
29 header::{self, ACCEPT},
30};
31use okapi::openapi3::OpenApi;
32
33use crate::*;
34
35#[openapi(
37 summary = "OpenAPI specification",
38 external_docs(url = "https://swagger.io/specification/"),
39 operation_id = "openapi_spec",
40 tags = "openapi",
41 responses(
42 ignore_return_type = true,
43 response(
44 status = "200",
45 description = "",
46 content = "axum::Json<std::collections::HashMap<String, String>>"
47 )
48 ),
49 crate = "crate"
50)]
51pub async fn serve_openapi_spec(spec: State<OpenApi>, headers: HeaderMap) -> Response {
52 let accept_header = headers
53 .get(ACCEPT)
54 .and_then(|h| h.to_str().ok())
55 .map(|h| h.to_ascii_lowercase());
56
57 match accept_header {
58 #[cfg(feature = "yaml")]
59 Some(accept_header) if accept_header.contains("yaml") => yaml::Yaml(spec.0).into_response(),
60 Some(accept_header) if accept_header.contains("json") | accept_header.contains("*/*") => {
61 Json(spec.0).into_response()
62 }
63 Some(_) => {
64 let status = StatusCode::BAD_REQUEST;
65 let headers = [(
66 header::CONTENT_TYPE,
67 HeaderValue::from_static("text/plain; charset=utf-8"),
68 )];
69 let err = if cfg!(feature = "yaml") {
70 "Bad Accept header value, should contain either 'json', 'yaml' or empty"
71 } else {
72 "Bad Accept header value, should contain either 'json' or empty"
73 };
74 (status, headers, err).into_response()
75 }
76 None => {
77 Json(spec.0).into_response()
79 }
80 }
81}
82
83#[rustfmt::skip]
86#[macro_export]
87macro_rules! openapi_handler {
88 ($($va:ident)::+ $(:: <$($gen_param:tt),+>)?) => {
90 $crate::openapi_handler!(@inner $($va)+; ; $($($gen_param)+)?)
91 };
92
93 (@inner $va:ident $($vb:ident)+ ; $(:: $acc:ident)*; $($gen_param:tt)*) => {
102 $crate::openapi_handler!(@inner $($vb)+; $(:: $acc)* :: $va; $($gen_param)*)
103 };
104 (@inner $va:ident ; $(:: $acc:ident)*; $($gen_param:tt)*) => {
105 $crate::openapi_handler!(@final $va; $($acc)::*; $($gen_param)*)
106 };
107
108 (@final $fn_name:ident ; $($prefix_path_part:ident)::* ; $($gen_param:tt)*) => {
114 $crate::axum_integration::paste!{
115 {
116 #[allow(unused_imports)]
117 use $crate::axum_integration::{HandlerExt, ServiceExt};
118
119 $($prefix_path_part ::)* $fn_name :: <$($gen_param),*>
120 .with_openapi($($prefix_path_part ::)* [<$fn_name __openapi>])
121 }
122 }
123 };
124}
125
126#[rustfmt::skip]
129#[macro_export]
130macro_rules! oh {
131 ($($v:tt)+) => {
132 $crate::openapi_handler!($($v)+)
133 };
134
135}
136
137#[rustfmt::skip]
140#[macro_export]
141#[deprecated = "Use `openapi_handler` instead"]
142macro_rules! openapi_service {
143 ($($t:tt)+) => {
144 {
145 $crate::openapi_handler!($($t)+)
146 }
147 }
148}
149
150