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