1use crate::openapi3::{Components, Info, OpenApi, PathItem, Responses, Tag};
2use crate::{Map, MapEntry};
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::fmt::Display;
6
7#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
8pub struct MergeError {
9 pub msg: String,
10}
11
12impl Display for MergeError {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 write!(f, "{}", self.msg)
15 }
16}
17
18impl MergeError {
19 fn new<S: AsRef<str>>(msg: S) -> Self {
20 MergeError {
21 msg: msg.as_ref().to_owned(),
22 }
23 }
24}
25
26impl OpenApi {
27 pub fn merge_spec<S: Display>(mut self, path_prefix: &S, s2: &Self) -> Result<(), MergeError> {
29 merge_specs(&mut self, path_prefix, s2)
30 }
31}
32
33pub fn marge_spec_list<S: Display>(spec_list: &[(S, OpenApi)]) -> Result<OpenApi, MergeError> {
35 let mut openapi_docs = OpenApi::new();
36 for (path_prefix, spec) in spec_list {
37 merge_specs(&mut openapi_docs, path_prefix, spec)?;
38 }
39 Ok(openapi_docs)
40}
41
42pub fn merge_specs<S: Display>(
44 s1: &mut OpenApi,
45 path_prefix: &S,
46 s2: &OpenApi,
47) -> Result<(), MergeError> {
48 if s1.openapi != s2.openapi {
50 return Err(MergeError::new("OpenAPI specs version do not match."));
51 }
52 merge_spec_info(&mut s1.info, &s2.info)?;
53 merge_vec(&mut s1.servers, &s2.servers);
54 merge_paths(&mut s1.paths, path_prefix, &s2.paths)?;
55 merge_components(&mut s1.components, &s2.components)?;
56 merge_vec(&mut s1.security, &s2.security);
59 merge_tags(&mut s1.tags, &s2.tags)?;
60 merge_option(&mut s1.external_docs, &s2.external_docs);
62 merge_map(&mut s1.extensions, &s2.extensions, "extensions");
63 Ok(())
64}
65
66pub fn merge_spec_info(s1: &mut Info, s2: &Info) -> Result<(), MergeError> {
67 s1.title = merge_string(&s1.title, &s2.title);
68 merge_opt_string(&mut s1.description, &s2.description);
69 merge_opt_string(&mut s1.terms_of_service, &s2.terms_of_service);
70 merge_option(&mut s1.contact, &s2.contact);
72 merge_option(&mut s1.license, &s2.license);
74 s1.version = merge_string(&s1.version, &s2.version);
75 merge_map(&mut s1.extensions, &s2.extensions, "extensions");
76 Ok(())
77}
78
79pub fn merge_paths<S: Display>(
83 s1: &mut Map<String, PathItem>,
84 path_prefix: &S,
85 s2: &Map<String, PathItem>,
86) -> Result<(), MergeError> {
87 for (key, value) in s2 {
90 let new_key = if key.starts_with('/') {
91 format!("{}{}", path_prefix, key)
92 } else {
93 log::error!(
94 "All routes should have a leading '/' but non found in `{}`.",
95 key
96 );
97 format!("{}/{}", path_prefix, key)
98 };
99 match s1.entry(new_key) {
100 MapEntry::Occupied(mut entry) => {
101 let current_value = entry.get_mut();
103 merge_path_item(current_value, value)?;
104 }
105 MapEntry::Vacant(entry) => {
106 entry.insert(value.clone());
107 }
108 }
109 }
110 Ok(())
111}
112
113pub fn merge_path_item(s1: &mut PathItem, s2: &PathItem) -> Result<(), MergeError> {
114 merge_opt_string(&mut s1.reference, &s2.reference);
115 merge_opt_string(&mut s1.summary, &s2.summary);
116 merge_opt_string(&mut s1.description, &s2.description);
117
118 merge_option(&mut s1.get, &s2.get);
119 merge_option(&mut s1.put, &s2.put);
120 merge_option(&mut s1.post, &s2.post);
121 merge_option(&mut s1.delete, &s2.delete);
122 merge_option(&mut s1.options, &s2.options);
123 merge_option(&mut s1.head, &s2.head);
124 merge_option(&mut s1.patch, &s2.patch);
125 merge_option(&mut s1.trace, &s2.trace);
126
127 merge_option(&mut s1.servers, &s2.servers);
128 merge_vec(&mut s1.parameters, &s2.parameters);
129 merge_map(&mut s1.extensions, &s2.extensions, "extensions");
130 Ok(())
131}
132
133pub fn merge_components(
134 s1: &mut Option<Components>,
135 s2: &Option<Components>,
136) -> Result<(), MergeError> {
137 if s1.is_none() {
138 *s1 = s2.clone();
139 Ok(())
140 } else if s2.is_none() {
141 Ok(())
143 } else {
144 if let Some(s1) = s1 {
145 let s2 = s2.as_ref().unwrap();
146 merge_map(&mut s1.schemas, &s2.schemas, "schemas");
147 merge_map(&mut s1.responses, &s2.responses, "responses");
148 merge_map(&mut s1.parameters, &s2.parameters, "parameters");
149 merge_map(&mut s1.examples, &s2.examples, "examples");
150 merge_map(&mut s1.request_bodies, &s2.request_bodies, "request_bodies");
151 merge_map(&mut s1.headers, &s2.headers, "headers");
152 merge_map(
153 &mut s1.security_schemes,
154 &s2.security_schemes,
155 "security_schemes",
156 );
157 merge_map(&mut s1.links, &s2.links, "links");
158 merge_map(&mut s1.callbacks, &s2.callbacks, "callbacks");
159 merge_map(&mut s1.extensions, &s2.extensions, "extensions");
160 }
161 Ok(())
162 }
163}
164
165pub fn merge_tags(s1: &mut Vec<Tag>, s2: &[Tag]) -> Result<Vec<Tag>, MergeError> {
166 let mut new_tags: Map<String, Tag> = Map::new();
168 for tag in s1 {
170 match new_tags.entry(tag.name.clone()) {
171 MapEntry::Occupied(mut entry) => {
172 let current_value = entry.get_mut();
173 merge_tag(current_value, tag)?;
174 }
175 MapEntry::Vacant(entry) => {
176 entry.insert(tag.clone());
177 }
178 }
179 }
180 for tag in s2 {
182 match new_tags.entry(tag.name.clone()) {
183 MapEntry::Occupied(mut entry) => {
184 let current_value = entry.get_mut();
185 merge_tag(current_value, tag)?;
186 }
187 MapEntry::Vacant(entry) => {
188 entry.insert(tag.clone());
189 }
190 }
191 }
192 let mut new_tags_vec = Vec::new();
194
195 let keys: Vec<String> = new_tags.keys().cloned().collect();
200 for key in keys {
201 if let Some(tag) = new_tags.remove(&key) {
202 new_tags_vec.push(tag);
203 } else {
204 unreachable!("List sizes or same list do not match.");
205 }
206 }
207 Ok(new_tags_vec)
208}
209
210pub fn merge_tag(s1: &mut Tag, s2: &Tag) -> Result<(), MergeError> {
211 if s1.name != s2.name {
212 return Err(MergeError::new("Tried to merge Tags with different names."));
213 }
214 merge_opt_string(&mut s1.description, &s2.description);
215 merge_option(&mut s1.external_docs, &s2.external_docs);
216 merge_map(&mut s1.extensions, &s2.extensions, "extensions");
217 Ok(())
218}
219
220pub fn merge_responses(s1: &mut Responses, s2: &Responses) -> Result<(), MergeError> {
221 merge_option(&mut s1.default, &s2.default);
222 merge_map(&mut s1.responses, &s2.responses, "responses");
223 merge_map(&mut s1.extensions, &s2.extensions, "extensions");
224 Ok(())
225}
226
227pub fn merge_string(s1: &str, s2: &str) -> String {
231 if s1.is_empty() {
232 s2.to_owned()
233 } else {
234 s1.to_owned()
235 }
236}
237
238pub fn merge_opt_string(s1: &mut Option<String>, s2: &Option<String>) {
243 if s1.is_none() {
244 *s1 = s2.clone();
245 } else if s1.is_some() && s2.is_some() {
246 *s1 = Some(merge_string(s1.as_ref().unwrap(), s2.as_ref().unwrap()))
247 }
248}
249
250pub fn merge_option<T: Clone>(s1: &mut Option<T>, s2: &Option<T>) {
254 if s1.is_none() {
255 *s1 = s2.clone();
256 }
257}
258
259pub fn merge_map<T: Clone + PartialEq + std::fmt::Debug>(
262 s1: &mut Map<String, T>,
263 s2: &Map<String, T>,
264 name: &str,
265) {
266 for (key, value) in s2 {
269 if let Some(s1_value) = s1.get(key) {
270 if value != s1_value {
272 log::warn!(
273 "Found conflicting {} keys while merging, \
274 they have the same name but different values for `{}`:\n\
275 {:?}\n\
276 {:?}",
277 name,
278 key,
279 s1_value,
280 value,
281 );
282 }
283 } else {
284 s1.insert(key.clone(), value.clone());
285 }
286 }
287}
288
289pub fn merge_vec<T: Clone>(s1: &mut Vec<T>, s2: &[T]) {
292 for value in s2 {
294 s1.push(value.clone());
295 }
296}