1use std::marker::PhantomData;
2
3use crate::{openapi::*, util::*};
4use indexmap::IndexMap;
5use serde::{Deserialize, Deserializer, Serialize};
6use tracing::warn;
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, schemars::JsonSchema)]
14pub struct PathItem {
15 #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
21 pub reference: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
25 pub summary: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
29 pub description: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub get: Option<Operation>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub put: Option<Operation>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub post: Option<Operation>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub delete: Option<Operation>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub options: Option<Operation>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub head: Option<Operation>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub patch: Option<Operation>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub trace: Option<Operation>,
46 #[serde(default, skip_serializing_if = "Vec::is_empty")]
48 pub servers: Vec<Server>,
49 #[serde(default)]
57 #[serde(skip_serializing_if = "Vec::is_empty")]
58 pub parameters: Vec<ReferenceOr<Parameter>>,
59 #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
61 pub extensions: IndexMap<String, serde_json::Value>,
62}
63
64impl PathItem {
65 pub fn iter(&self) -> impl Iterator<Item = (&str, &'_ Operation)> {
67 vec![
68 ("get", &self.get),
69 ("put", &self.put),
70 ("post", &self.post),
71 ("delete", &self.delete),
72 ("options", &self.options),
73 ("head", &self.head),
74 ("patch", &self.patch),
75 ("trace", &self.trace),
76 ]
77 .into_iter()
78 .filter_map(|(method, maybe_op)| maybe_op.as_ref().map(|op| (method, op)))
79 }
80
81 pub fn merge(mut self, other: Self) -> Self {
83 self.merge_with(other);
84 self
85 }
86
87 pub fn merge_with(&mut self, mut other: Self) {
89 self.servers.append(&mut other.servers);
90
91 self.parameters.append(&mut other.parameters);
92
93 for (k, ext) in other.extensions {
94 self.extensions
95 .entry(k.clone())
96 .and_modify(|_| warn!("Conflict on merging extension {}", k))
97 .or_insert(ext);
98 }
99 macro_rules! merge {
100 ($id:ident) => {
101 if let Some($id) = other.$id {
102 if self.$id.is_some() {
103 warn!(
104 "Conflict on merging {}, ignoring duplicate",
105 stringify!($id)
106 );
107 } else {
108 let _ = self.$id.insert($id);
109 }
110 }
111 };
112 }
113 merge!(reference);
114 merge!(summary);
115 merge!(description);
116 merge!(get);
117 merge!(put);
118 merge!(post);
119 merge!(delete);
120 merge!(options);
121 merge!(head);
122 merge!(patch);
123 merge!(trace);
124 }
125}
126
127impl IntoIterator for PathItem {
128 type Item = (&'static str, Operation);
129
130 type IntoIter = std::vec::IntoIter<Self::Item>;
131
132 fn into_iter(self) -> Self::IntoIter {
134 vec![
135 ("get", self.get),
136 ("put", self.put),
137 ("post", self.post),
138 ("delete", self.delete),
139 ("options", self.options),
140 ("head", self.head),
141 ("patch", self.patch),
142 ("trace", self.trace),
143 ]
144 .into_iter()
145 .filter_map(|(method, maybe_op)| maybe_op.map(|op| (method, op)))
146 .collect::<Vec<_>>()
147 .into_iter()
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, schemars::JsonSchema)]
156pub struct Paths {
157 #[serde(flatten, deserialize_with = "deserialize_paths")]
159 pub paths: IndexMap<String, ReferenceOr<PathItem>>,
160 #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
162 pub extensions: IndexMap<String, serde_json::Value>,
163}
164
165impl Paths {
166 pub fn iter(&self) -> indexmap::map::Iter<'_, String, ReferenceOr<PathItem>> {
168 self.paths.iter()
169 }
170}
171
172impl IntoIterator for Paths {
173 type Item = (String, ReferenceOr<PathItem>);
174
175 type IntoIter = indexmap::map::IntoIter<String, ReferenceOr<PathItem>>;
176
177 fn into_iter(self) -> Self::IntoIter {
178 self.paths.into_iter()
179 }
180}
181
182fn deserialize_paths<'de, D>(
183 deserializer: D,
184) -> Result<IndexMap<String, ReferenceOr<PathItem>>, D::Error>
185where
186 D: Deserializer<'de>,
187{
188 deserializer.deserialize_map(PredicateVisitor(
189 |key: &String| key.starts_with('/'),
190 PhantomData,
191 ))
192}