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 #[must_use]
31 pub fn new() -> Self {
32 Default::default()
33 }
34 #[must_use]
36 pub fn path<K: Into<String>, V: Into<PathItem>>(mut self, key: K, value: V) -> Self {
37 self.insert(key, value);
38 self
39 }
40 pub fn insert<K: Into<String>, V: Into<PathItem>>(&mut self, key: K, value: V) {
42 let key = key.into();
43 let mut value = value.into();
44 self.0
45 .entry(key)
46 .and_modify(|item| {
47 if value.summary.is_some() {
48 item.summary = value.summary.take();
49 }
50 if value.description.is_some() {
51 item.description = value.description.take();
52 }
53 item.servers.append(&mut value.servers);
54 item.parameters.append(&mut value.parameters);
55 item.operations.append(&mut value.operations);
56 })
57 .or_insert(value);
58 }
59 pub fn append(&mut self, other: &mut Self) {
64 let items = std::mem::take(&mut other.0);
65 for item in items {
66 self.insert(item.0, item.1);
67 }
68 }
69 pub fn extend<I, K, V>(&mut self, iter: I)
71 where
72 I: IntoIterator<Item = (K, V)>,
73 K: Into<String>,
74 V: Into<PathItem>,
75 {
76 for (k, v) in iter.into_iter() {
77 self.insert(k, v);
78 }
79 }
80}
81
82#[non_exhaustive]
87#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)]
88#[serde(rename_all = "camelCase")]
89pub struct PathItem {
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub summary: Option<String>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
97 pub description: Option<String>,
98
99 #[serde(skip_serializing_if = "Servers::is_empty")]
102 pub servers: Servers,
103
104 #[serde(skip_serializing_if = "Parameters::is_empty")]
108 #[serde(flatten)]
109 pub parameters: Parameters,
110
111 #[serde(flatten)]
114 pub operations: Operations,
115}
116
117impl PathItem {
118 pub fn new<O: Into<Operation>>(path_item_type: PathItemType, operation: O) -> Self {
120 let operations = PropMap::from_iter(iter::once((path_item_type, operation.into())));
121
122 Self {
123 operations: Operations(operations),
124 ..Default::default()
125 }
126 }
127 pub fn append(&mut self, other: &mut Self) {
132 self.operations.append(&mut other.operations);
133 self.servers.append(&mut other.servers);
134 self.parameters.append(&mut other.parameters);
135 if other.description.is_some() {
136 self.description = other.description.take();
137 }
138 if other.summary.is_some() {
139 self.summary = other.summary.take();
140 }
141 }
142
143 #[must_use]
146 pub fn add_operation<O: Into<Operation>>(
147 mut self,
148 path_item_type: PathItemType,
149 operation: O,
150 ) -> Self {
151 self.operations.insert(path_item_type, operation.into());
152 self
153 }
154
155 #[must_use]
157 pub fn summary<S: Into<String>>(mut self, summary: S) -> Self {
158 self.summary = Some(summary.into());
159 self
160 }
161
162 #[must_use]
165 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
166 self.description = Some(description.into());
167 self
168 }
169
170 #[must_use]
173 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: I) -> Self {
174 self.servers = Servers(servers.into_iter().collect());
175 self
176 }
177
178 #[must_use]
180 pub fn parameters<I: IntoIterator<Item = Parameter>>(mut self, parameters: I) -> Self {
181 self.parameters = Parameters(parameters.into_iter().collect());
182 self
183 }
184}
185
186#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy, Debug)]
188#[serde(rename_all = "lowercase")]
189pub enum PathItemType {
190 Get,
192 Post,
194 Put,
196 Delete,
198 Options,
200 Head,
202 Patch,
204 Trace,
206 Connect,
208}
209
210#[cfg(test)]
211mod tests {
212 use assert_json_diff::assert_json_eq;
213 use serde_json::json;
214
215 use super::*;
216 use crate::oapi::response::Response;
217
218 #[test]
219 fn test_build_path_item() {
220 let path_item = PathItem::new(PathItemType::Get, Operation::new())
221 .summary("summary")
222 .description("description")
223 .servers(Servers::new())
224 .parameters(Parameters::new());
225
226 assert_json_eq!(
227 path_item,
228 json!({
229 "description": "description",
230 "summary": "summary",
231 "get": {
232 "responses": {}
233 }
234 })
235 )
236 }
237
238 #[test]
239 fn test_path_item_append() {
240 let mut path_item = PathItem::new(
241 PathItemType::Get,
242 Operation::new().add_response("200", Response::new("Get success")),
243 );
244 let mut other_path_item = PathItem::new(
245 PathItemType::Post,
246 Operation::new().add_response("200", Response::new("Post success")),
247 )
248 .description("description")
249 .summary("summary");
250 path_item.append(&mut other_path_item);
251
252 assert_json_eq!(
253 path_item,
254 json!({
255 "description": "description",
256 "summary": "summary",
257 "get": {
258 "responses": {
259 "200": {
260 "description": "Get success"
261 }
262 }
263 },
264 "post": {
265 "responses": {
266 "200": {
267 "description": "Post success"
268 }
269 }
270 }
271 })
272 )
273 }
274
275 #[test]
276 fn test_path_item_add_operation() {
277 let path_item = PathItem::new(
278 PathItemType::Get,
279 Operation::new().add_response("200", Response::new("Get success")),
280 )
281 .add_operation(
282 PathItemType::Post,
283 Operation::new().add_response("200", Response::new("Post success")),
284 );
285
286 assert_json_eq!(
287 path_item,
288 json!({
289 "get": {
290 "responses": {
291 "200": {
292 "description": "Get success"
293 }
294 }
295 },
296 "post": {
297 "responses": {
298 "200": {
299 "description": "Post success"
300 }
301 }
302 }
303 })
304 )
305 }
306
307 #[test]
308 fn test_paths_extend() {
309 let mut paths = Paths::new().path(
310 "/api/do_something",
311 PathItem::new(
312 PathItemType::Get,
313 Operation::new().add_response("200", Response::new("Get success")),
314 ),
315 );
316 paths.extend([(
317 "/api/do_something",
318 PathItem::new(
319 PathItemType::Post,
320 Operation::new().add_response("200", Response::new("Post success")),
321 )
322 .summary("summary")
323 .description("description"),
324 )]);
325
326 assert_json_eq!(
327 paths,
328 json!({
329 "/api/do_something": {
330 "description": "description",
331 "summary": "summary",
332 "get": {
333 "responses": {
334 "200": {
335 "description": "Get success"
336 }
337 }
338 },
339 "post": {
340 "responses": {
341 "200": {
342 "description": "Post success"
343 }
344 }
345 }
346 }
347 })
348 );
349 }
350
351 #[test]
352 fn test_paths_deref() {
353 let paths = Paths::new();
354 assert_eq!(0, paths.len());
355 }
356}