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 #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
118 pub extensions: PropMap<String, serde_json::Value>,
119}
120
121impl PathItem {
122 pub fn new<O: Into<Operation>>(path_item_type: PathItemType, operation: O) -> Self {
124 let operations = PropMap::from_iter(iter::once((path_item_type, operation.into())));
125
126 Self {
127 operations: Operations(operations),
128 ..Default::default()
129 }
130 }
131 pub fn append(&mut self, other: &mut Self) {
136 self.operations.append(&mut other.operations);
137 self.servers.append(&mut other.servers);
138 self.parameters.append(&mut other.parameters);
139 if other.description.is_some() {
140 self.description = other.description.take();
141 }
142 if other.summary.is_some() {
143 self.summary = other.summary.take();
144 }
145 other
146 .extensions
147 .retain(|name, _| !self.extensions.contains_key(name));
148 self.extensions.append(&mut other.extensions);
149 }
150
151 #[must_use]
154 pub fn add_operation<O: Into<Operation>>(
155 mut self,
156 path_item_type: PathItemType,
157 operation: O,
158 ) -> Self {
159 self.operations.insert(path_item_type, operation.into());
160 self
161 }
162
163 #[must_use]
165 pub fn summary<S: Into<String>>(mut self, summary: S) -> Self {
166 self.summary = Some(summary.into());
167 self
168 }
169
170 #[must_use]
173 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
174 self.description = Some(description.into());
175 self
176 }
177
178 #[must_use]
181 pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: I) -> Self {
182 self.servers = Servers(servers.into_iter().collect());
183 self
184 }
185
186 #[must_use]
188 pub fn parameters<I: IntoIterator<Item = Parameter>>(mut self, parameters: I) -> Self {
189 self.parameters = Parameters(parameters.into_iter().collect());
190 self
191 }
192
193 #[must_use]
195 pub fn extensions(mut self, extensions: PropMap<String, serde_json::Value>) -> Self {
196 self.extensions = extensions;
197 self
198 }
199}
200
201#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy, Debug)]
203#[serde(rename_all = "lowercase")]
204pub enum PathItemType {
205 Get,
207 Post,
209 Put,
211 Delete,
213 Options,
215 Head,
217 Patch,
219 Trace,
221 Connect,
223}
224
225#[cfg(test)]
226mod tests {
227 use assert_json_diff::assert_json_eq;
228 use serde_json::json;
229
230 use super::*;
231 use crate::oapi::response::Response;
232
233 #[test]
234 fn test_build_path_item() {
235 let path_item = PathItem::new(PathItemType::Get, Operation::new())
236 .summary("summary")
237 .description("description")
238 .servers(Servers::new())
239 .parameters(Parameters::new());
240
241 assert_json_eq!(
242 path_item,
243 json!({
244 "description": "description",
245 "summary": "summary",
246 "get": {
247 "responses": {}
248 }
249 })
250 )
251 }
252
253 #[test]
254 fn test_path_item_append() {
255 let mut path_item = PathItem::new(
256 PathItemType::Get,
257 Operation::new().add_response("200", Response::new("Get success")),
258 );
259 let mut other_path_item = PathItem::new(
260 PathItemType::Post,
261 Operation::new().add_response("200", Response::new("Post success")),
262 )
263 .description("description")
264 .summary("summary");
265 path_item.append(&mut other_path_item);
266
267 assert_json_eq!(
268 path_item,
269 json!({
270 "description": "description",
271 "summary": "summary",
272 "get": {
273 "responses": {
274 "200": {
275 "description": "Get success"
276 }
277 }
278 },
279 "post": {
280 "responses": {
281 "200": {
282 "description": "Post success"
283 }
284 }
285 }
286 })
287 )
288 }
289
290 #[test]
291 fn test_path_item_add_operation() {
292 let path_item = PathItem::new(
293 PathItemType::Get,
294 Operation::new().add_response("200", Response::new("Get success")),
295 )
296 .add_operation(
297 PathItemType::Post,
298 Operation::new().add_response("200", Response::new("Post success")),
299 );
300
301 assert_json_eq!(
302 path_item,
303 json!({
304 "get": {
305 "responses": {
306 "200": {
307 "description": "Get success"
308 }
309 }
310 },
311 "post": {
312 "responses": {
313 "200": {
314 "description": "Post success"
315 }
316 }
317 }
318 })
319 )
320 }
321
322 #[test]
323 fn test_paths_extend() {
324 let mut paths = Paths::new().path(
325 "/api/do_something",
326 PathItem::new(
327 PathItemType::Get,
328 Operation::new().add_response("200", Response::new("Get success")),
329 ),
330 );
331 paths.extend([(
332 "/api/do_something",
333 PathItem::new(
334 PathItemType::Post,
335 Operation::new().add_response("200", Response::new("Post success")),
336 )
337 .summary("summary")
338 .description("description"),
339 )]);
340
341 assert_json_eq!(
342 paths,
343 json!({
344 "/api/do_something": {
345 "description": "description",
346 "summary": "summary",
347 "get": {
348 "responses": {
349 "200": {
350 "description": "Get success"
351 }
352 }
353 },
354 "post": {
355 "responses": {
356 "200": {
357 "description": "Post success"
358 }
359 }
360 }
361 }
362 })
363 );
364 }
365
366 #[test]
367 fn test_paths_deref() {
368 let paths = Paths::new();
369 assert_eq!(0, paths.len());
370 }
371}