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