1use crate::*;
2use indexmap::IndexMap;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6pub struct OpenAPI {
7 pub openapi: String,
13 pub info: Info,
16 #[serde(default, skip_serializing_if = "Vec::is_empty")]
20 pub servers: Vec<Server>,
21 pub paths: Paths,
23 #[serde(default, skip_serializing_if = "Components::is_empty")]
25 pub components: Components,
26 #[serde(default, skip_serializing_if = "Vec::is_empty")]
33 pub security: Vec<SecurityRequirement>,
34 #[serde(default, skip_serializing_if = "Vec::is_empty")]
41 pub tags: Vec<Tag>,
42 #[serde(rename = "externalDocs", skip_serializing_if = "Option::is_none")]
44 pub external_docs: Option<ExternalDocumentation>,
45 #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
47 pub extensions: IndexMap<String, serde_json::Value>,
48}
49
50impl std::ops::Deref for OpenAPI {
51 type Target = Components;
52
53 fn deref(&self) -> &Self::Target {
54 &self.components
55 }
56}
57
58impl std::ops::DerefMut for OpenAPI {
59 fn deref_mut(&mut self) -> &mut Self::Target {
60 &mut self.components
61 }
62}
63
64impl OpenAPI {
65 pub fn operations(&self) -> impl Iterator<Item=(&str, &str, &Operation, &PathItem)> {
72 self.paths
73 .iter()
74 .filter_map(|(path, item)| item.as_item().map(|i| (path, i)))
75 .flat_map(|(path, item)| {
76 item.iter()
77 .map(move |(method, op)| (path.as_str(), method, op, item))
78 })
79 }
80
81 pub fn operations_mut(&mut self) -> impl Iterator<Item=(&str, &str, &mut Operation)> {
82 self.paths
83 .iter_mut()
84 .filter_map(|(path, item)| item.as_mut().map(|i| (path, i)))
85 .flat_map(|(path, item)| {
86 item.iter_mut()
87 .map(move |(method, op)| (path.as_str(), method, op))
88 })
89 }
90
91 pub fn get_operation_mut(&mut self, operation_id: &str) -> Option<&mut Operation> {
92 self.operations_mut()
93 .find(|(_, _, op)| op.operation_id.as_ref().unwrap() == operation_id)
94 .map(|(_, _, op)| op)
95 }
96
97 pub fn get_operation(&self, operation_id: &str) -> Option<(&Operation, &PathItem)> {
98 self.operations()
99 .find(|(_, _, op, _)| op.operation_id.as_ref().unwrap() == operation_id)
100 .map(|(_, _, op, item)| (op, item))
101 }
102
103 pub fn merge(mut self, other: OpenAPI) -> Result<Self, MergeError> {
106 merge_map(&mut self.info.extensions, other.info.extensions);
107
108 merge_vec(&mut self.servers, other.servers, |a, b| a.url == b.url);
109
110 for (path, item) in other.paths {
111 let item = item.into_item().ok_or_else(|| MergeError::new("PathItem references are not yet supported. Please opena n issue if you need this feature."))?;
112 if self.paths.paths.contains_key(&path) {
113 let self_item = self.paths.paths.get_mut(&path).unwrap().as_mut().ok_or_else(|| MergeError::new("PathItem references are not yet supported. Please open an issue if you need this feature."))?;
114 option_or(&mut self_item.get, item.get);
115 option_or(&mut self_item.put, item.put);
116 option_or(&mut self_item.post, item.post);
117 option_or(&mut self_item.delete, item.delete);
118 option_or(&mut self_item.options, item.options);
119 option_or(&mut self_item.head, item.head);
120 option_or(&mut self_item.patch, item.patch);
121 option_or(&mut self_item.trace, item.trace);
122
123 merge_vec(&mut self_item.servers, item.servers, |a, b| a.url == b.url);
124 merge_map(&mut self_item.extensions, item.extensions);
125
126 if self_item.parameters.len() != item.parameters.len() {
127 return Err(MergeError(format!("PathItem {} parameters do not have the same length", path)));
128 }
129 for (a, b) in self_item.parameters.iter_mut().zip(item.parameters) {
130 let a = a.as_item().ok_or_else(|| MergeError::new("Parameter references are not yet supported. Please open an issue if you need this feature."))?;
131 let b = b.as_item().ok_or_else(|| MergeError::new("Parameter references are not yet supported. Please open an issue if you need this feature."))?;
132 if a.name != b.name {
133 return Err(MergeError(format!("PathItem {} parameter {} does not have the same name as {}", path, a.name, b.name)));
134 }
135 }
136 } else {
137 self.paths.paths.insert(path, RefOr::Item(item));
138 }
139 }
140
141 merge_map(&mut self.components.extensions, other.components.extensions);
142 merge_map(&mut self.components.schemas, other.components.schemas.into());
143 merge_map(&mut self.components.responses, other.components.responses.into());
144 merge_map(&mut self.components.parameters, other.components.parameters.into());
145 merge_map(&mut self.components.examples, other.components.examples.into());
146 merge_map(&mut self.components.request_bodies, other.components.request_bodies.into());
147 merge_map(&mut self.components.headers, other.components.headers.into());
148 merge_map(&mut self.components.security_schemes, other.components.security_schemes.into());
149 merge_map(&mut self.components.links, other.components.links.into());
150 merge_map(&mut self.components.callbacks, other.components.callbacks.into());
151
152 merge_vec(&mut self.security, other.security, |a, b| {
153 if a.len() != b.len() {
154 return false;
155 }
156 a.iter().all(|(a, _)| b.contains_key(a))
157 });
158 merge_vec(&mut self.tags, other.tags, |a, b| a.name == b.name);
159
160 match self.external_docs.as_mut() {
161 Some(ext) => {
162 if let Some(other) = other.external_docs {
163 merge_map(&mut ext.extensions, other.extensions)
164 }
165 }
166 None => self.external_docs = other.external_docs
167 }
168
169 merge_map(&mut self.extensions, other.extensions);
170 Ok(self)
171 }
172
173 pub fn merge_overwrite(self, other: OpenAPI) -> Result<Self, MergeError> {
176 other.merge(self)
177 }
178}
179
180impl Default for OpenAPI {
181 fn default() -> Self {
182 OpenAPI {
184 openapi: "3.0.3".to_string(),
185 info: default(),
186 servers: default(),
187 paths: default(),
188 components: default(),
189 security: default(),
190 tags: default(),
191 external_docs: default(),
192 extensions: default(),
193 }
194 }
195}
196
197fn merge_vec<T>(original: &mut Vec<T>, mut other: Vec<T>, cmp: fn(&T, &T) -> bool) {
198 other.retain(|o| !original.iter().any(|r| cmp(o, r)));
199 original.extend(other);
200}
201
202fn merge_map<K, V>(original: &mut IndexMap<K, V>, mut other: IndexMap<K, V>) where K: Eq + std::hash::Hash {
203 other.retain(|k, _| !original.contains_key(k));
204 original.extend(other);
205}
206
207fn option_or<T>(original: &mut Option<T>, other: Option<T>) {
208 if original.is_none() {
209 *original = other;
210 }
211}
212
213#[derive(Debug)]
214pub struct MergeError(String);
215
216impl MergeError {
217 pub fn new(msg: &str) -> Self {
218 MergeError(msg.to_string())
219 }
220}
221
222impl std::error::Error for MergeError {}
223
224impl std::fmt::Display for MergeError {
225 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
226 write!(f, "{}", self.0)
227 }
228}
229
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_merge_basic() {
237 let mut a = OpenAPI::default();
238 a.servers.push(Server {
239 url: "http://localhost".to_string(),
240 ..Server::default()
241 });
242 let mut b = OpenAPI::default();
243 b.servers.push(Server {
244 url: "http://localhost".to_string(),
245 ..Server::default()
246 });
247 a = a.merge(b).unwrap();
248 assert_eq!(a.servers.len(), 1);
249 }
250}