kynos_openapi/model/response/
mod.rs1pub mod status;
4
5use std::fmt;
6
7use serde::{
8 Deserialize, Deserializer, Serialize, Serializer,
9 de::{Error as DeError, MapAccess, Visitor},
10 ser::SerializeMap,
11};
12use serde_json::Value;
13
14use crate::{
15 Map,
16 model::{
17 body::media_type::MediaType, extensions::Extensions, link::Link, parameter::header::Header,
18 reference::RefOr, response::status::StatusPattern,
19 },
20};
21
22#[derive(Clone, Debug, Default, PartialEq)]
28pub struct Responses {
29 pub default_response: Option<RefOr<Response>>,
31
32 pub responses: Map<RefOr<Response>>,
34
35 pub extensions: Extensions,
37}
38
39impl Responses {
40 #[must_use]
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 #[must_use]
51 pub fn with(mut self, status: u16, response: Response) -> Self {
52 self.responses.insert(
53 StatusPattern::Code(status).to_string(),
54 RefOr::Item(response),
55 );
56 self
57 }
58
59 #[must_use]
61 pub fn with_pattern(mut self, pattern: StatusPattern, response: RefOr<Response>) -> Self {
62 self.responses.insert(pattern.to_string(), response);
63 self
64 }
65
66 #[must_use]
68 pub fn with_default(mut self, response: Response) -> Self {
69 self.default_response = Some(RefOr::Item(response));
70 self
71 }
72
73 #[must_use]
84 pub fn is_empty(&self) -> bool {
85 self.default_response.is_none() && self.responses.is_empty() && self.extensions.is_empty()
86 }
87
88 #[must_use]
94 pub fn declares_a_response(&self) -> bool {
95 self.default_response.is_some() || !self.responses.is_empty()
96 }
97
98 #[must_use]
103 pub fn get(&self, status: u16) -> Option<&RefOr<Response>> {
104 self.responses.get(&StatusPattern::Code(status).to_string())
105 }
106
107 pub fn merge_from(&mut self, other: &Self) {
111 if self.default_response.is_none() {
112 self.default_response.clone_from(&other.default_response);
113 }
114 for (key, response) in &other.responses {
115 if !self.responses.contains_key(key) {
116 self.responses.insert(key.clone(), response.clone());
117 }
118 }
119 }
120}
121
122impl Serialize for Responses {
123 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
124 let len = usize::from(self.default_response.is_some())
125 + self.responses.len()
126 + self.extensions.0.len();
127 let mut map = serializer.serialize_map(Some(len))?;
128 if let Some(default) = &self.default_response {
129 map.serialize_entry("default", default)?;
130 }
131 for (key, response) in &self.responses {
132 map.serialize_entry(key, response)?;
133 }
134 for (key, value) in &self.extensions.0 {
135 map.serialize_entry(key, value)?;
136 }
137 map.end()
138 }
139}
140
141impl<'de> Deserialize<'de> for Responses {
142 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
143 struct ResponsesVisitor;
144
145 impl<'de> Visitor<'de> for ResponsesVisitor {
146 type Value = Responses;
147
148 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 f.write_str("a map of status patterns to responses")
150 }
151
152 fn visit_map<A: MapAccess<'de>>(self, mut access: A) -> Result<Responses, A::Error> {
153 let mut responses = Responses::new();
154 while let Some(key) = access.next_key::<String>()? {
155 if key == "default" {
156 responses.default_response = Some(access.next_value()?);
157 } else if key.starts_with(crate::model::extensions::EXTENSION_PREFIX) {
158 responses.extensions.0.insert(key, access.next_value()?);
159 } else {
160 key.parse::<StatusPattern>().map_err(A::Error::custom)?;
163 responses.responses.insert(key, access.next_value()?);
164 }
165 }
166 Ok(responses)
167 }
168 }
169
170 deserializer.deserialize_map(ResponsesVisitor)
171 }
172}
173
174#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
176pub struct Response {
177 #[cfg(feature = "openapi32")]
182 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub summary: Option<String>,
184
185 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub description: Option<String>,
201
202 #[serde(default, skip_serializing_if = "Map::is_empty")]
207 pub headers: Map<RefOr<Header>>,
208
209 #[serde(default, skip_serializing_if = "Map::is_empty")]
211 pub content: Map<MediaType>,
212
213 #[serde(default, skip_serializing_if = "Map::is_empty")]
215 pub links: Map<RefOr<Link>>,
216
217 #[serde(flatten)]
219 pub extensions: Extensions,
220}
221
222impl Response {
223 pub fn new(description: impl Into<String>) -> Self {
225 Self {
226 description: Some(description.into()),
227 ..Self::default()
228 }
229 }
230
231 pub fn with_content(
233 description: impl Into<String>,
234 media_type: impl Into<String>,
235 content: MediaType,
236 ) -> Self {
237 let mut response = Self::new(description);
238 response.content.insert(media_type.into(), content);
239 response
240 }
241
242 #[must_use]
244 pub fn with_header(mut self, name: impl Into<String>, header: Header) -> Self {
245 self.headers.insert(name.into(), RefOr::Item(header));
246 self
247 }
248
249 #[must_use]
251 pub fn with_link(mut self, name: impl Into<String>, link: Link) -> Self {
252 self.links.insert(name.into(), RefOr::Item(link));
253 self
254 }
255
256 #[must_use]
258 pub fn with_extension(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
259 self.extensions.insert(key, value);
260 self
261 }
262}
263
264#[cfg(test)]
265mod tests;