1use std::ops::{Deref, DerefMut};
5
6use indexmap::IndexMap;
7use serde::{Deserialize, Serialize};
8
9use crate::{PropMap, Ref, RefOr};
10
11use super::link::Link;
12use super::{Content, header::Header};
13
14#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
20#[serde(rename_all = "camelCase")]
21pub struct Responses(PropMap<String, RefOr<Response>>);
22
23impl<K, R> From<PropMap<K, R>> for Responses
24where
25 K: Into<String>,
26 R: Into<RefOr<Response>>,
27{
28 fn from(inner: PropMap<K, R>) -> Self {
29 Self(
30 inner
31 .into_iter()
32 .map(|(k, v)| (k.into(), v.into()))
33 .collect(),
34 )
35 }
36}
37impl<K, R, const N: usize> From<[(K, R); N]> for Responses
38where
39 K: Into<String>,
40 R: Into<RefOr<Response>>,
41{
42 fn from(inner: [(K, R); N]) -> Self {
43 Self(
44 <[(K, R)]>::into_vec(Box::new(inner))
45 .into_iter()
46 .map(|(k, v)| (k.into(), v.into()))
47 .collect(),
48 )
49 }
50}
51
52impl Deref for Responses {
53 type Target = PropMap<String, RefOr<Response>>;
54
55 fn deref(&self) -> &Self::Target {
56 &self.0
57 }
58}
59
60impl DerefMut for Responses {
61 fn deref_mut(&mut self) -> &mut Self::Target {
62 &mut self.0
63 }
64}
65
66impl IntoIterator for Responses {
67 type Item = (String, RefOr<Response>);
68 type IntoIter = <PropMap<String, RefOr<Response>> as IntoIterator>::IntoIter;
69
70 fn into_iter(self) -> Self::IntoIter {
71 self.0.into_iter()
72 }
73}
74
75impl Responses {
76 pub fn new() -> Self {
78 Default::default()
79 }
80 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
82 mut self,
83 key: S,
84 response: R,
85 ) -> Self {
86 self.insert(key, response);
87 self
88 }
89
90 pub fn insert<S: Into<String>, R: Into<RefOr<Response>>>(&mut self, key: S, response: R) {
92 self.0.insert(key.into(), response.into());
93 }
94
95 pub fn append(&mut self, other: &mut Responses) {
100 self.0.append(&mut other.0);
101 }
102
103 pub fn extend<I, C, R>(&mut self, iter: I)
105 where
106 I: IntoIterator<Item = (C, R)>,
107 C: Into<String>,
108 R: Into<RefOr<Response>>,
109 {
110 self.0.extend(
111 iter.into_iter()
112 .map(|(key, response)| (key.into(), response.into())),
113 );
114 }
115}
116
117impl From<Responses> for PropMap<String, RefOr<Response>> {
118 fn from(responses: Responses) -> Self {
119 responses.0
120 }
121}
122
123impl<C, R> FromIterator<(C, R)> for Responses
124where
125 C: Into<String>,
126 R: Into<RefOr<Response>>,
127{
128 fn from_iter<T: IntoIterator<Item = (C, R)>>(iter: T) -> Self {
129 Self(PropMap::from_iter(
130 iter.into_iter()
131 .map(|(key, response)| (key.into(), response.into())),
132 ))
133 }
134}
135
136#[non_exhaustive]
142#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
143#[serde(rename_all = "camelCase")]
144pub struct Response {
145 pub description: String,
147
148 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
150 pub headers: PropMap<String, Header>,
151
152 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
157 #[serde(rename = "content")]
158 pub contents: IndexMap<String, Content>,
159
160 #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
162 pub extensions: PropMap<String, serde_json::Value>,
163
164 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
167 pub links: PropMap<String, RefOr<Link>>,
168}
169
170impl Response {
171 pub fn new<S: Into<String>>(description: S) -> Self {
175 Self {
176 description: description.into(),
177 ..Default::default()
178 }
179 }
180
181 pub fn description<I: Into<String>>(mut self, description: I) -> Self {
183 self.description = description.into();
184 self
185 }
186
187 pub fn add_content<S: Into<String>, C: Into<Content>>(mut self, key: S, content: C) -> Self {
189 self.contents.insert(key.into(), content.into());
190 self
191 }
192 pub fn add_header<S: Into<String>>(mut self, name: S, header: Header) -> Self {
194 self.headers.insert(name.into(), header);
195 self
196 }
197
198 pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
200 self.extensions.insert(key.into(), value);
201 self
202 }
203
204 pub fn add_link<S: Into<String>, L: Into<RefOr<Link>>>(mut self, name: S, link: L) -> Self {
206 self.links.insert(name.into(), link.into());
207
208 self
209 }
210}
211
212impl From<Ref> for RefOr<Response> {
213 fn from(r: Ref) -> Self {
214 Self::Ref(r)
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::{Content, Header, PropMap, Ref, RefOr, Response, Responses};
221 use assert_json_diff::assert_json_eq;
222 use serde_json::json;
223
224 #[test]
225 fn responses_new() {
226 let responses = Responses::new();
227 assert!(responses.is_empty());
228 }
229
230 #[test]
231 fn response_builder() -> Result<(), serde_json::Error> {
232 let request_body = Response::new("A sample response")
233 .description("A sample response description")
234 .add_content(
235 "application/json",
236 Content::new(Ref::from_schema_name("MySchemaPayload")),
237 )
238 .add_header(
239 "content-type",
240 Header::default().description("application/json"),
241 );
242
243 assert_json_eq!(
244 request_body,
245 json!({
246 "description": "A sample response description",
247 "content": {
248 "application/json": {
249 "schema": {
250 "$ref": "#/components/schemas/MySchemaPayload"
251 }
252 }
253 },
254 "headers": {
255 "content-type": {
256 "description": "application/json",
257 "schema": {
258 "type": "string"
259 }
260 }
261 }
262 })
263 );
264 Ok(())
265 }
266
267 #[test]
268 fn test_responses_from_btree_map() {
269 let input = PropMap::from([
270 ("response1".to_string(), Response::new("response1")),
271 ("response2".to_string(), Response::new("response2")),
272 ]);
273
274 let expected = Responses(PropMap::from([
275 (
276 "response1".to_string(),
277 RefOr::Type(Response::new("response1")),
278 ),
279 (
280 "response2".to_string(),
281 RefOr::Type(Response::new("response2")),
282 ),
283 ]));
284
285 let actual = Responses::from(input);
286
287 assert_eq!(expected, actual);
288 }
289
290 #[test]
291 fn test_responses_from_kv_sequence() {
292 let input = [
293 ("response1".to_string(), Response::new("response1")),
294 ("response2".to_string(), Response::new("response2")),
295 ];
296
297 let expected = Responses(PropMap::from([
298 (
299 "response1".to_string(),
300 RefOr::Type(Response::new("response1")),
301 ),
302 (
303 "response2".to_string(),
304 RefOr::Type(Response::new("response2")),
305 ),
306 ]));
307
308 let actual = Responses::from(input);
309
310 assert_eq!(expected, actual);
311 }
312
313 #[test]
314 fn test_responses_from_iter() {
315 let input = [
316 ("response1".to_string(), Response::new("response1")),
317 ("response2".to_string(), Response::new("response2")),
318 ];
319
320 let expected = Responses(PropMap::from([
321 (
322 "response1".to_string(),
323 RefOr::Type(Response::new("response1")),
324 ),
325 (
326 "response2".to_string(),
327 RefOr::Type(Response::new("response2")),
328 ),
329 ]));
330
331 let actual = Responses::from_iter(input);
332
333 assert_eq!(expected, actual);
334 }
335
336 #[test]
337 fn test_responses_into_iter() {
338 let responses = Responses::new();
339 let responses = responses.response("response1", Response::new("response1"));
340 assert_eq!(1, responses.into_iter().collect::<Vec<_>>().len());
341 }
342
343 #[test]
344 fn test_btree_map_from_responses() {
345 let expected = PropMap::from([
346 (
347 "response1".to_string(),
348 RefOr::Type(Response::new("response1")),
349 ),
350 (
351 "response2".to_string(),
352 RefOr::Type(Response::new("response2")),
353 ),
354 ]);
355
356 let actual = PropMap::from(
357 Responses::new()
358 .response("response1", Response::new("response1"))
359 .response("response2", Response::new("response2")),
360 );
361 assert_eq!(expected, actual);
362 }
363}