1use std::ops::{Deref, DerefMut};
5
6use indexmap::IndexMap;
7use serde::{Deserialize, Serialize};
8
9use super::Content;
10use super::header::Header;
11use super::link::Link;
12use crate::{PropMap, Ref, RefOr};
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 #[must_use]
79 pub fn new() -> Self {
80 Default::default()
81 }
82 #[must_use]
84 pub fn response<S: Into<String>, R: Into<RefOr<Response>>>(
85 mut self,
86 key: S,
87 response: R,
88 ) -> Self {
89 self.insert(key, response);
90 self
91 }
92
93 pub fn insert<S: Into<String>, R: Into<RefOr<Response>>>(&mut self, key: S, response: R) {
95 self.0.insert(key.into(), response.into());
96 }
97
98 pub fn append(&mut self, other: &mut Self) {
103 self.0.append(&mut other.0);
104 }
105
106 pub fn extend<I, C, R>(&mut self, iter: I)
108 where
109 I: IntoIterator<Item = (C, R)>,
110 C: Into<String>,
111 R: Into<RefOr<Response>>,
112 {
113 self.0.extend(
114 iter.into_iter()
115 .map(|(key, response)| (key.into(), response.into())),
116 );
117 }
118}
119
120impl From<Responses> for PropMap<String, RefOr<Response>> {
121 fn from(responses: Responses) -> Self {
122 responses.0
123 }
124}
125
126impl<C, R> FromIterator<(C, R)> for Responses
127where
128 C: Into<String>,
129 R: Into<RefOr<Response>>,
130{
131 fn from_iter<T: IntoIterator<Item = (C, R)>>(iter: T) -> Self {
132 Self(PropMap::from_iter(
133 iter.into_iter()
134 .map(|(key, response)| (key.into(), response.into())),
135 ))
136 }
137}
138
139#[non_exhaustive]
145#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
146#[serde(rename_all = "camelCase")]
147pub struct Response {
148 pub description: String,
150
151 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
153 pub headers: PropMap<String, Header>,
154
155 #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
161 #[serde(rename = "content")]
162 pub contents: IndexMap<String, Content>,
163
164 #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
166 pub extensions: PropMap<String, serde_json::Value>,
167
168 #[serde(skip_serializing_if = "PropMap::is_empty", default)]
171 pub links: PropMap<String, RefOr<Link>>,
172}
173
174impl Response {
175 #[must_use]
179 pub fn new<S: Into<String>>(description: S) -> Self {
180 Self {
181 description: description.into(),
182 ..Default::default()
183 }
184 }
185
186 #[must_use]
188 pub fn description<I: Into<String>>(mut self, description: I) -> Self {
189 self.description = description.into();
190 self
191 }
192
193 #[must_use]
196 pub fn add_content<S: Into<String>, C: Into<Content>>(mut self, key: S, content: C) -> Self {
197 self.contents.insert(key.into(), content.into());
198 self
199 }
200 #[must_use]
202 pub fn add_header<S: Into<String>>(mut self, name: S, header: Header) -> Self {
203 self.headers.insert(name.into(), header);
204 self
205 }
206
207 #[must_use]
209 pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
210 self.extensions.insert(key.into(), value);
211 self
212 }
213
214 #[must_use]
216 pub fn add_link<S: Into<String>, L: Into<RefOr<Link>>>(mut self, name: S, link: L) -> Self {
217 self.links.insert(name.into(), link.into());
218
219 self
220 }
221}
222
223impl From<Ref> for RefOr<Response> {
224 fn from(r: Ref) -> Self {
225 Self::Ref(r)
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use assert_json_diff::assert_json_eq;
232 use serde_json::json;
233
234 use super::{Content, Header, PropMap, Ref, RefOr, Response, Responses};
235
236 #[test]
237 fn responses_new() {
238 let responses = Responses::new();
239 assert!(responses.is_empty());
240 }
241
242 #[test]
243 fn response_builder() -> Result<(), serde_json::Error> {
244 let request_body = Response::new("A sample response")
245 .description("A sample response description")
246 .add_content(
247 "application/json",
248 Content::new(Ref::from_schema_name("MySchemaPayload")),
249 )
250 .add_header(
251 "content-type",
252 Header::default().description("application/json"),
253 );
254
255 assert_json_eq!(
256 request_body,
257 json!({
258 "description": "A sample response description",
259 "content": {
260 "application/json": {
261 "schema": {
262 "$ref": "#/components/schemas/MySchemaPayload"
263 }
264 }
265 },
266 "headers": {
267 "content-type": {
268 "description": "application/json",
269 "schema": {
270 "type": "string"
271 }
272 }
273 }
274 })
275 );
276 Ok(())
277 }
278
279 #[test]
280 fn test_responses_from_btree_map() {
281 let input = PropMap::from([
282 ("response1".to_owned(), Response::new("response1")),
283 ("response2".to_owned(), Response::new("response2")),
284 ]);
285
286 let expected = Responses(PropMap::from([
287 (
288 "response1".to_owned(),
289 RefOr::Type(Response::new("response1")),
290 ),
291 (
292 "response2".to_owned(),
293 RefOr::Type(Response::new("response2")),
294 ),
295 ]));
296
297 let actual = Responses::from(input);
298
299 assert_eq!(expected, actual);
300 }
301
302 #[test]
303 fn test_responses_from_kv_sequence() {
304 let input = [
305 ("response1".to_owned(), Response::new("response1")),
306 ("response2".to_owned(), Response::new("response2")),
307 ];
308
309 let expected = Responses(PropMap::from([
310 (
311 "response1".to_owned(),
312 RefOr::Type(Response::new("response1")),
313 ),
314 (
315 "response2".to_owned(),
316 RefOr::Type(Response::new("response2")),
317 ),
318 ]));
319
320 let actual = Responses::from(input);
321
322 assert_eq!(expected, actual);
323 }
324
325 #[test]
326 fn test_responses_from_iter() {
327 let input = [
328 ("response1".to_owned(), Response::new("response1")),
329 ("response2".to_owned(), Response::new("response2")),
330 ];
331
332 let expected = Responses(PropMap::from([
333 (
334 "response1".to_owned(),
335 RefOr::Type(Response::new("response1")),
336 ),
337 (
338 "response2".to_owned(),
339 RefOr::Type(Response::new("response2")),
340 ),
341 ]));
342
343 let actual = Responses::from_iter(input);
344
345 assert_eq!(expected, actual);
346 }
347
348 #[test]
349 fn test_responses_into_iter() {
350 let responses = Responses::new();
351 let responses = responses.response("response1", Response::new("response1"));
352 assert_eq!(1, responses.into_iter().collect::<Vec<_>>().len());
353 }
354
355 #[test]
356 fn test_btree_map_from_responses() {
357 let expected = PropMap::from([
358 (
359 "response1".to_owned(),
360 RefOr::Type(Response::new("response1")),
361 ),
362 (
363 "response2".to_owned(),
364 RefOr::Type(Response::new("response2")),
365 ),
366 ]);
367
368 let actual = PropMap::from(
369 Responses::new()
370 .response("response1", Response::new("response1"))
371 .response("response2", Response::new("response2")),
372 );
373 assert_eq!(expected, actual);
374 }
375}