okapi_operation/
to_responses.rs1use okapi::openapi3::{RefOr, Responses};
2
3use crate::Components;
4
5pub trait ToResponses {
7 fn generate(components: &mut Components) -> Result<Responses, anyhow::Error>;
8}
9
10#[macro_export]
24macro_rules! impl_to_responses_for_wrapper {
25 ($ty:path) => {
26 impl<T: $crate::schemars::JsonSchema> $crate::ToResponses for $ty {
27 fn generate(components: &mut $crate::Components) -> Result<$crate::okapi::openapi3::Responses, $crate::anyhow::Error> {
28 let media_types = <$ty as $crate::ToMediaTypes>::generate(components)?;
29 Ok($crate::okapi::openapi3::Responses {
30 responses: $crate::okapi::map! {
31 "200".into() => $crate::okapi::openapi3::RefOr::Object(
32 $crate::okapi::openapi3::Response { content: media_types, ..Default::default() }
33 )
34 },
35 ..Default::default()
36 })
37 }
38 }
39 };
40}
41
42macro_rules! forward_impl_to_responses {
43 ($ty_for:ty, $ty_base:ty) => {
44 impl $crate::ToResponses for $ty_for {
45 fn generate(
46 components: &mut $crate::Components,
47 ) -> Result<$crate::okapi::openapi3::Responses, $crate::anyhow::Error> {
48 <$ty_base as $crate::ToResponses>::generate(components)
49 }
50 }
51 };
52}
53
54mod impls {
55 use std::borrow::Cow;
56
57 use bytes::{Bytes, BytesMut};
58 use okapi::openapi3::Response;
59
60 use super::*;
61 use crate::ToMediaTypes;
62
63 impl ToResponses for () {
64 fn generate(_components: &mut Components) -> Result<Responses, anyhow::Error> {
65 Ok(Responses {
66 responses: okapi::map! {
67 "200".into() => RefOr::Object(Default::default())
68 },
69 ..Default::default()
70 })
71 }
72 }
73
74 impl<T, E> ToResponses for Result<T, E>
75 where
76 T: ToResponses,
77 E: ToResponses,
78 {
79 fn generate(components: &mut Components) -> Result<Responses, anyhow::Error> {
80 let overlap_err_fn = |status| {
81 anyhow::anyhow!(
82 "Type {} produces {} response in both Ok and Err variants",
83 std::any::type_name::<Self>(),
84 status
85 )
86 };
87 let mut ok = T::generate(components)?;
88 let err = E::generate(components)?;
89
90 if ok.default.is_some() && err.default.is_some() {
91 return Err(overlap_err_fn("default"));
92 }
93 ok.default = ok.default.or(err.default);
94
95 for (status, response) in err.responses.into_iter() {
96 if ok.responses.contains_key(&status) {
97 return Err(overlap_err_fn(&status));
98 }
99 let _ = ok.responses.insert(status, response);
100 }
101
102 Ok(ok)
103 }
104 }
105
106 impl ToResponses for String {
107 fn generate(components: &mut Components) -> Result<Responses, anyhow::Error> {
108 Ok(Responses {
109 responses: okapi::map! {
110 "200".into() => RefOr::Object(Response {
111 content: <Self as ToMediaTypes>::generate(components)?,
112 ..Default::default()
113 })
114 },
115 ..Default::default()
116 })
117 }
118 }
119 forward_impl_to_responses!(&'static str, String);
120 forward_impl_to_responses!(Cow<'static, str>, String);
121
122 impl ToResponses for Vec<u8> {
123 fn generate(components: &mut Components) -> Result<Responses, anyhow::Error> {
124 Ok(Responses {
125 responses: okapi::map! {
126 "200".into() => RefOr::Object(Response {
127 content: <Self as ToMediaTypes>::generate(components)?,
128 ..Default::default()
129 })
130 },
131 ..Default::default()
132 })
133 }
134 }
135 forward_impl_to_responses!(&'static [u8], Vec<u8>);
136 forward_impl_to_responses!(Cow<'static, [u8]>, Vec<u8>);
137 forward_impl_to_responses!(Bytes, Vec<u8>);
138 forward_impl_to_responses!(BytesMut, Vec<u8>);
139}