1use std::iter;
5use std::ops::{Deref, DerefMut};
6
7use serde::{Deserialize, Serialize};
8
9use super::{Operation, Operations, Parameter, Parameters, PathMap, PropMap, Server, Servers};
10
11#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
15pub struct Paths(PathMap<String, PathItem>);
16impl Deref for Paths {
17 type Target = PathMap<String, PathItem>;
18
19 fn deref(&self) -> &Self::Target {
20 &self.0
21 }
22}
23impl DerefMut for Paths {
24 fn deref_mut(&mut self) -> &mut Self::Target {
25 &mut self.0
26 }
27}
28impl Paths {
29 pub fn new() -> Self {
31 Default::default()
32 }
33 pub fn path<K: Into<String>, V: Into<PathItem>>(mut self, key: K, value: V) -> Self {
35 self.insert(key, value);
36 self
37 }
38 pub fn insert<K: Into<String>, V: Into<PathItem>>(&mut self, key: K, value: V) {
40 let key = key.into();
41 let mut value = value.into();
42 self.0
43 .entry(key)
44 .and_modify(|item| {
45 if value.summary.is_some() {
46 item.summary = value.summary.take();
47 }
48 if value.description.is_some() {
49 item.description = value.description.take();
50 }
51 item.servers.append(&mut value.servers);
52 item.parameters.append(&mut value.parameters);
53 item.operations.append(&mut value.operations);
54 })
55 .or_insert(value);
56 }
57 pub fn append(&mut self, other: &mut Paths) {
62 let items = std::mem::take(&mut other.0);
63 for item in items {
64 self.insert(item.0, item.1);
65 }
66 }
67 pub fn extend<I, K, V>(&mut self, iter: I)
69 where
70 I: IntoIterator<Item = (K, V)>,
71 K: Into<String>,
72 V: Into<PathItem>,
73 {
74 for (k, v) in iter.into_iter() {
75 self.insert(k, v);
76 }
77 }
78}
79
80#[non_exhaustive]
85#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
86#[serde(rename_all = "camelCase")]
87pub struct PathItem {
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub summary: Option<String>,
91
92 #[serde(skip_serializing_if = "Option::is_none")]
95 pub description: Option<String>,
96
97 #[serde(skip_serializing_if = "Servers::is_empty")]
100 pub servers: Servers,
101
102 #[serde(skip_serializing_if = "Parameters::is_empty")]
106 #[serde(flatten)]
107 pub parameters: Parameters,
108
109 #[serde(flatten)]
112 pub operations: Operations,
113}
114
115impl PathItem {
116 pub fn new<O: Into<Operation>>(path_item_type: PathItemType, operation: O) -> Self {
118 let operations = PropMap::from_iter(iter::once((path_item_type, operation.into())));
119
120 Self {
121 operations: Operations(operations),
122 ..Default::default()
123 }
124 }
125 pub fn append(&mut self, other: &mut Self) {
130 self.operations.append(&mut other.operations);
131 self.servers.append(&mut other.servers);
132 self.parameters.append(&mut other.parameters);
133 if other.description.is_some() {
134 self.description = other.description.take();
135 }
136 if other.summary.is_some() {
137 self.summary = other.summary.take();
138 }
139 }
140
141 pub fn add_operation<O: Into<Operation>>(
144 mut self,
145 path_item_type: PathItemType,
146 operation: O,
147 ) -> Self {
148 self.operations.insert(path_item_type, operation.into());
149 self
150 }
151
152 pub fn summary<S: Into<String>>(mut self, summary: S) -> Self {
154 self.summary = Some(summary.into());
155 self
156 }
157
158 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
161 self.description = Some(description.into());
162 self
163 }
164
165 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: I) -> Self {
168 self.servers = Servers(servers.into_iter().collect());
169 self
170 }
171
172 pub fn parameters<I: IntoIterator<Item = Parameter>>(mut self, parameters: I) -> Self {
174 self.parameters = Parameters(parameters.into_iter().collect());
175 self
176 }
177}
178
179#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy, Debug)]
181#[serde(rename_all = "lowercase")]
182pub enum PathItemType {
183 Get,
185 Post,
187 Put,
189 Delete,
191 Options,
193 Head,
195 Patch,
197 Trace,
199 Connect,
201}
202
203#[cfg(test)]
204mod tests {
205 use assert_json_diff::assert_json_eq;
206 use serde_json::json;
207
208 use super::*;
209 use crate::oapi::response::Response;
210
211 #[test]
212 fn test_build_path_item() {
213 let path_item = PathItem::new(PathItemType::Get, Operation::new())
214 .summary("summary")
215 .description("description")
216 .servers(Servers::new())
217 .parameters(Parameters::new());
218
219 assert_json_eq!(
220 path_item,
221 json!({
222 "description": "description",
223 "summary": "summary",
224 "get": {
225 "responses": {}
226 }
227 })
228 )
229 }
230
231 #[test]
232 fn test_path_item_append() {
233 let mut path_item = PathItem::new(
234 PathItemType::Get,
235 Operation::new().add_response("200", Response::new("Get success")),
236 );
237 let mut other_path_item = PathItem::new(
238 PathItemType::Post,
239 Operation::new().add_response("200", Response::new("Post success")),
240 )
241 .description("description")
242 .summary("summary");
243 path_item.append(&mut other_path_item);
244
245 assert_json_eq!(
246 path_item,
247 json!({
248 "description": "description",
249 "summary": "summary",
250 "get": {
251 "responses": {
252 "200": {
253 "description": "Get success"
254 }
255 }
256 },
257 "post": {
258 "responses": {
259 "200": {
260 "description": "Post success"
261 }
262 }
263 }
264 })
265 )
266 }
267
268 #[test]
269 fn test_path_item_add_operation() {
270 let path_item = PathItem::new(
271 PathItemType::Get,
272 Operation::new().add_response("200", Response::new("Get success")),
273 )
274 .add_operation(
275 PathItemType::Post,
276 Operation::new().add_response("200", Response::new("Post success")),
277 );
278
279 assert_json_eq!(
280 path_item,
281 json!({
282 "get": {
283 "responses": {
284 "200": {
285 "description": "Get success"
286 }
287 }
288 },
289 "post": {
290 "responses": {
291 "200": {
292 "description": "Post success"
293 }
294 }
295 }
296 })
297 )
298 }
299
300 #[test]
301 fn test_paths_extend() {
302 let mut paths = Paths::new().path(
303 "/api/do_something",
304 PathItem::new(
305 PathItemType::Get,
306 Operation::new().add_response("200", Response::new("Get success")),
307 ),
308 );
309 paths.extend([(
310 "/api/do_something",
311 PathItem::new(
312 PathItemType::Post,
313 Operation::new().add_response("200", Response::new("Post success")),
314 )
315 .summary("summary")
316 .description("description"),
317 )]);
318
319 assert_json_eq!(
320 paths,
321 json!({
322 "/api/do_something": {
323 "description": "description",
324 "summary": "summary",
325 "get": {
326 "responses": {
327 "200": {
328 "description": "Get success"
329 }
330 }
331 },
332 "post": {
333 "responses": {
334 "200": {
335 "description": "Post success"
336 }
337 }
338 }
339 }
340 })
341 );
342 }
343
344 #[test]
345 fn test_paths_deref() {
346 let paths = Paths::new();
347 assert_eq!(0, paths.len());
348 }
349}