1use serde_json::Value;
2use std::borrow::Borrow;
3use std::cell::RefCell;
4use std::collections::{BTreeMap, HashMap};
5use std::rc::Rc;
6use std::sync::Arc;
7
8use crate::core::{
9 ComponentContainer, DiffCache, DiffContext, DiffResult, MayBeRefCore,
10};
11use crate::schema::{
12 Components, Example, Header, HttpSchema, Link, Parameter, Path,
13 RequestBody, Response, Schema, SecurityScheme,
14};
15use crate::schema_diff::{
16 ExampleDiff, HeaderDiff, LinkDiff, ParameterDiff, PathDiff,
17 RequestBodyDiff, ResponseDiff, SchemaDiff, SecuritySchemeDiff,
18};
19
20#[derive(Clone)]
21pub struct HttpSchemaDiffContext {
22 depth: usize,
23 direct: bool,
24
25 source: Rc<HttpSchema>,
26 target: Rc<HttpSchema>,
27
28 source_visited_references: Rc<BTreeMap<String, usize>>,
29 target_visited_references: Rc<BTreeMap<String, usize>>,
30
31 schema_diff_cache:
32 Rc<RefCell<HashMap<String, Arc<DiffResult<SchemaDiff>>>>>,
33 header_diff_cache:
34 Rc<RefCell<HashMap<String, Arc<DiffResult<HeaderDiff>>>>>,
35 response_diff_cache:
36 Rc<RefCell<HashMap<String, Arc<DiffResult<ResponseDiff>>>>>,
37 parameter_diff_cache:
38 Rc<RefCell<HashMap<String, Arc<DiffResult<ParameterDiff>>>>>,
39
40 example_diff_cache:
41 Rc<RefCell<HashMap<String, Arc<DiffResult<ExampleDiff>>>>>,
42 request_body_diff_cache:
43 Rc<RefCell<HashMap<String, Arc<DiffResult<RequestBodyDiff>>>>>,
44
45 link_diff_cache: Rc<RefCell<HashMap<String, Arc<DiffResult<LinkDiff>>>>>,
46 security_scheme_diff_cache:
47 Rc<RefCell<HashMap<String, Arc<DiffResult<SecuritySchemeDiff>>>>>,
48}
49
50impl HttpSchemaDiffContext {
51 pub fn new(source: Rc<HttpSchema>, target: Rc<HttpSchema>) -> Self {
52 Self {
53 depth: 0,
54 direct: true,
55
56 source,
57 target,
58
59 schema_diff_cache: Rc::new(RefCell::new(HashMap::new())),
60 header_diff_cache: Rc::new(RefCell::new(HashMap::new())),
61 response_diff_cache: Rc::new(RefCell::new(HashMap::new())),
62 parameter_diff_cache: Rc::new(RefCell::new(HashMap::new())),
63
64 example_diff_cache: Rc::new(RefCell::new(HashMap::new())),
65 request_body_diff_cache: Rc::new(RefCell::new(HashMap::new())),
66 link_diff_cache: Rc::new(RefCell::new(HashMap::new())),
67 security_scheme_diff_cache: Rc::new(RefCell::new(HashMap::new())),
68
69 source_visited_references: Rc::new(BTreeMap::new()),
70 target_visited_references: Rc::new(BTreeMap::new()),
71 }
72 }
73}
74
75impl DiffContext for HttpSchemaDiffContext {
76 fn removing(&self) -> HttpSchemaDiffContext {
77 Self {
78 source: Rc::clone(&self.source),
79 target: Rc::clone(&self.target),
80
81 depth: self.depth,
82 direct: self.direct,
83
84 schema_diff_cache: Rc::clone(&self.schema_diff_cache),
85 header_diff_cache: Rc::clone(&self.header_diff_cache),
86 response_diff_cache: Rc::clone(&self.response_diff_cache),
87 parameter_diff_cache: Rc::clone(&self.parameter_diff_cache),
88
89 example_diff_cache: Rc::clone(&self.example_diff_cache),
90 request_body_diff_cache: Rc::clone(&self.request_body_diff_cache),
91 link_diff_cache: Rc::clone(&self.link_diff_cache),
92 security_scheme_diff_cache: Rc::clone(
93 &self.security_scheme_diff_cache,
94 ),
95
96 source_visited_references: Rc::clone(
97 &self.source_visited_references,
98 ),
99 target_visited_references: Rc::clone(
100 &self.target_visited_references,
101 ),
102 }
103 }
104
105 fn switch_flow(&self) -> HttpSchemaDiffContext {
106 Self {
107 depth: self.depth,
108 direct: !self.direct,
109
110 source: Rc::clone(&self.source),
111 target: Rc::clone(&self.target),
112
113 source_visited_references: Rc::clone(
114 &self.source_visited_references,
115 ),
116 target_visited_references: Rc::clone(
117 &self.target_visited_references,
118 ),
119
120 schema_diff_cache: Rc::clone(&self.schema_diff_cache),
121 header_diff_cache: Rc::clone(&self.header_diff_cache),
122 response_diff_cache: Rc::clone(&self.response_diff_cache),
123 parameter_diff_cache: Rc::clone(&self.parameter_diff_cache),
124
125 example_diff_cache: Rc::clone(&self.example_diff_cache),
126 request_body_diff_cache: Rc::clone(&self.request_body_diff_cache),
127 link_diff_cache: Rc::clone(&self.link_diff_cache),
128 security_scheme_diff_cache: Rc::clone(
129 &self.security_scheme_diff_cache,
130 ),
131 }
132 }
133
134 fn is_direct_flow(&self) -> bool {
135 self.direct
136 }
137
138 fn add_visited_reference_source(&self, reference: &str) -> Self {
139 let mut source_visited_references =
140 (*self.source_visited_references).clone();
141 source_visited_references
142 .entry(reference.to_owned())
143 .and_modify(|count| *count += 1)
144 .or_insert(1);
145
146 Self {
147 depth: self.depth,
148 direct: self.direct,
149
150 source: Rc::clone(&self.source),
151 target: Rc::clone(&self.target),
152
153 source_visited_references: Rc::new(source_visited_references),
154 target_visited_references: Rc::clone(
155 &self.target_visited_references,
156 ),
157
158 schema_diff_cache: Rc::clone(&self.schema_diff_cache),
159 header_diff_cache: Rc::clone(&self.header_diff_cache),
160 response_diff_cache: Rc::clone(&self.response_diff_cache),
161 parameter_diff_cache: Rc::clone(&self.parameter_diff_cache),
162
163 example_diff_cache: Rc::clone(&self.example_diff_cache),
164 request_body_diff_cache: Rc::clone(&self.request_body_diff_cache),
165 link_diff_cache: Rc::clone(&self.link_diff_cache),
166 security_scheme_diff_cache: Rc::clone(
167 &self.security_scheme_diff_cache,
168 ),
169 }
170 }
171
172 fn check_visited_reference_source(&self, reference: &str) -> usize {
173 *self.source_visited_references.get(reference).unwrap_or(&0)
174 }
175
176 fn add_visited_reference_target(&self, reference: &str) -> Self {
177 let mut target_visited_references =
178 (*self.target_visited_references).clone();
179 target_visited_references
180 .entry(reference.to_owned())
181 .and_modify(|count| *count += 1)
182 .or_insert(1);
183
184 Self {
185 depth: self.depth,
186 direct: self.direct,
187
188 source: Rc::clone(&self.source),
189 target: Rc::clone(&self.target),
190
191 source_visited_references: Rc::clone(
192 &self.source_visited_references,
193 ),
194 target_visited_references: Rc::new(target_visited_references),
195
196 schema_diff_cache: Rc::clone(&self.schema_diff_cache),
197 header_diff_cache: Rc::clone(&self.header_diff_cache),
198 response_diff_cache: Rc::clone(&self.response_diff_cache),
199 parameter_diff_cache: Rc::clone(&self.parameter_diff_cache),
200
201 example_diff_cache: Rc::clone(&self.example_diff_cache),
202 request_body_diff_cache: Rc::clone(&self.request_body_diff_cache),
203 link_diff_cache: Rc::clone(&self.link_diff_cache),
204 security_scheme_diff_cache: Rc::clone(
205 &self.security_scheme_diff_cache,
206 ),
207 }
208 }
209
210 fn check_visited_reference_target(&self, reference: &str) -> usize {
211 *self.target_visited_references.get(reference).unwrap_or(&0)
212 }
213}
214
215pub fn deref_schema<'a>(
216 components: &'a Option<Components>,
217 reference: &str,
218) -> Option<&'a Schema> {
219 components.as_ref().and_then(|components| {
220 components.schemas.as_ref().and_then(|map| {
221 map.get(&reference.replace("#/components/schemas/", ""))
222 .map(|may_be_schema| match may_be_schema {
223 MayBeRefCore::Value(value) => value,
224 _ => unimplemented!(),
225 })
226 })
227 })
228}
229
230pub fn deref_parameter<'a>(
231 components: &'a Option<Components>,
232 reference: &str,
233) -> Option<&'a Parameter> {
234 components.as_ref().and_then(|components| {
235 components.parameters.as_ref().and_then(|map| {
236 map.get(&reference.replace("#/components/parameters/", ""))
237 .map(|may_be_parameter| match may_be_parameter {
238 MayBeRefCore::Value(value) => value,
239 _ => unimplemented!(),
240 })
241 })
242 })
243}
244
245pub fn deref_example<'a>(
246 schema: &'a HttpSchema,
247 reference: &str,
248) -> Option<&'a Example> {
249 schema.components.as_ref().and_then(|components| {
250 components.examples.as_ref().and_then(|map| {
251 map.get(&reference.replace("#/components/examples/", ""))
252 .map(|may_be_example| match may_be_example {
253 MayBeRefCore::Value(value) => value,
254 _ => unimplemented!(),
255 })
256 })
257 })
258}
259
260pub fn deref_request_body<'a>(
261 schema: &'a HttpSchema,
262 reference: &str,
263) -> Option<&'a RequestBody> {
264 schema.components.as_ref().and_then(|components| {
265 components.request_bodies.as_ref().and_then(|map| {
266 map.get(&reference.replace("#/components/requestBodies/", ""))
267 .map(|may_be_request_body| match may_be_request_body {
268 MayBeRefCore::Value(value) => value,
269 _ => unimplemented!(),
270 })
271 })
272 })
273}
274
275pub fn deref_header<'a>(
276 schema: &'a HttpSchema,
277 reference: &str,
278) -> Option<&'a Header> {
279 schema.components.as_ref().and_then(|components| {
280 components.headers.as_ref().and_then(|map| {
281 map.get(&reference.replace("#/components/headers/", ""))
282 .map(|may_be_header| match may_be_header {
283 MayBeRefCore::Value(value) => value,
284 _ => unimplemented!(),
285 })
286 })
287 })
288}
289
290pub fn deref_security_scheme<'a>(
291 schema: &'a HttpSchema,
292 reference: &str,
293) -> Option<&'a SecurityScheme> {
294 schema.components.as_ref().and_then(|components| {
295 components.security_schemes.as_ref().and_then(|map| {
296 map.get(&reference.replace("#/components/securitySchemes/", ""))
297 .map(|may_be_security_scheme| match may_be_security_scheme {
298 MayBeRefCore::Value(value) => value,
299 _ => unimplemented!(),
300 })
301 })
302 })
303}
304
305pub fn deref_link<'a>(
306 schema: &'a HttpSchema,
307 reference: &str,
308) -> Option<&'a Link> {
309 schema.components.as_ref().and_then(|components| {
310 components.links.as_ref().and_then(|map| {
311 map.get(&reference.replace("#/components/links/", "")).map(
312 |may_be_link| match may_be_link {
313 MayBeRefCore::Value(value) => value,
314 _ => unimplemented!(),
315 },
316 )
317 })
318 })
319}
320
321pub fn deref_response<'a>(
322 schema: &'a HttpSchema,
323 reference: &str,
324) -> Option<&'a Response> {
325 schema.components.as_ref().and_then(|components| {
326 components.responses.as_ref().and_then(|map| {
327 map.get(&reference.replace("#/components/responses/", ""))
328 .map(|may_be_response| match may_be_response {
329 MayBeRefCore::Value(value) => value,
330 _ => unimplemented!(),
331 })
332 })
333 })
334}
335
336impl ComponentContainer<Path> for HttpSchemaDiffContext {
337 fn deref_source(&self, _reference: &str) -> Option<&Path> {
338 None
339 }
340
341 fn deref_target(&self, _reference: &str) -> Option<&Path> {
342 None
343 }
344}
345
346impl ComponentContainer<Value> for HttpSchemaDiffContext {
347 fn deref_source(&self, _reference: &str) -> Option<&Value> {
348 None
349 }
350
351 fn deref_target(&self, _reference: &str) -> Option<&Value> {
352 None
353 }
354}
355
356impl ComponentContainer<Schema> for HttpSchemaDiffContext {
357 fn deref_source(&self, reference: &str) -> Option<&Schema> {
358 deref_schema(&self.source.components, reference)
359 }
360
361 fn deref_target(&self, reference: &str) -> Option<&Schema> {
362 deref_schema(&self.target.components, reference)
363 }
364}
365
366impl ComponentContainer<Parameter> for HttpSchemaDiffContext {
367 fn deref_source(&self, reference: &str) -> Option<&Parameter> {
368 deref_parameter(&self.source.components, reference)
369 }
370
371 fn deref_target(&self, reference: &str) -> Option<&Parameter> {
372 deref_parameter(&self.target.components, reference)
373 }
374}
375
376impl ComponentContainer<Example> for HttpSchemaDiffContext {
377 fn deref_source(&self, reference: &str) -> Option<&Example> {
378 deref_example(self.source.borrow(), reference)
379 }
380
381 fn deref_target(&self, reference: &str) -> Option<&Example> {
382 deref_example(self.target.borrow(), reference)
383 }
384}
385
386impl ComponentContainer<RequestBody> for HttpSchemaDiffContext {
387 fn deref_source(&self, reference: &str) -> Option<&RequestBody> {
388 deref_request_body(self.source.borrow(), reference)
389 }
390
391 fn deref_target(&self, reference: &str) -> Option<&RequestBody> {
392 deref_request_body(self.target.borrow(), reference)
393 }
394}
395
396impl ComponentContainer<Header> for HttpSchemaDiffContext {
397 fn deref_source(&self, reference: &str) -> Option<&Header> {
398 deref_header(self.source.borrow(), reference)
399 }
400
401 fn deref_target(&self, reference: &str) -> Option<&Header> {
402 deref_header(self.target.borrow(), reference)
403 }
404}
405
406impl ComponentContainer<SecurityScheme> for HttpSchemaDiffContext {
407 fn deref_source(&self, reference: &str) -> Option<&SecurityScheme> {
408 deref_security_scheme(self.source.borrow(), reference)
409 }
410
411 fn deref_target(&self, reference: &str) -> Option<&SecurityScheme> {
412 deref_security_scheme(self.target.borrow(), reference)
413 }
414}
415
416impl ComponentContainer<Link> for HttpSchemaDiffContext {
417 fn deref_source(&self, reference: &str) -> Option<&Link> {
418 deref_link(self.source.borrow(), reference)
419 }
420
421 fn deref_target(&self, reference: &str) -> Option<&Link> {
422 deref_link(self.target.borrow(), reference)
423 }
424}
425
426impl ComponentContainer<Response> for HttpSchemaDiffContext {
427 fn deref_source(&self, reference: &str) -> Option<&Response> {
428 deref_response(self.source.borrow(), reference)
429 }
430
431 fn deref_target(&self, reference: &str) -> Option<&Response> {
432 deref_response(self.target.borrow(), reference)
433 }
434}
435
436impl DiffCache<PathDiff> for HttpSchemaDiffContext {
437 fn get_diff(&self, _reference: &str) -> Option<Arc<DiffResult<PathDiff>>> {
438 None
439 }
440
441 fn set_diff(
442 &self,
443 _reference: &str,
444 _component: Arc<DiffResult<PathDiff>>,
445 ) {
446 }
447}
448
449impl DiffCache<Value> for HttpSchemaDiffContext {
450 fn get_diff(&self, _reference: &str) -> Option<Arc<DiffResult<Value>>> {
451 None
452 }
453
454 fn set_diff(&self, _reference: &str, _component: Arc<DiffResult<Value>>) {}
455}
456
457impl DiffCache<SchemaDiff> for HttpSchemaDiffContext {
458 fn get_diff(
459 &self,
460 reference: &str,
461 ) -> Option<Arc<DiffResult<SchemaDiff>>> {
462 (*self.schema_diff_cache)
463 .borrow()
464 .get(reference)
465 .map(Arc::clone)
466 }
467
468 fn set_diff(
469 &self,
470 reference: &str,
471 component: Arc<DiffResult<SchemaDiff>>,
472 ) {
473 let _ = RefCell::borrow_mut(&*self.schema_diff_cache)
474 .insert(reference.to_string(), component);
475 }
476}
477
478impl DiffCache<ParameterDiff> for HttpSchemaDiffContext {
479 fn get_diff(
480 &self,
481 reference: &str,
482 ) -> Option<Arc<DiffResult<ParameterDiff>>> {
483 (*self.parameter_diff_cache)
484 .borrow()
485 .get(reference)
486 .map(Arc::clone)
487 }
488
489 fn set_diff(
490 &self,
491 reference: &str,
492 component: Arc<DiffResult<ParameterDiff>>,
493 ) {
494 let _ = RefCell::borrow_mut(&*self.parameter_diff_cache)
495 .insert(reference.to_string(), component);
496 }
497}
498
499impl DiffCache<HeaderDiff> for HttpSchemaDiffContext {
500 fn get_diff(
501 &self,
502 reference: &str,
503 ) -> Option<Arc<DiffResult<HeaderDiff>>> {
504 (*self.header_diff_cache)
505 .borrow()
506 .get(reference)
507 .map(Arc::clone)
508 }
509
510 fn set_diff(
511 &self,
512 reference: &str,
513 component: Arc<DiffResult<HeaderDiff>>,
514 ) {
515 let _ = RefCell::borrow_mut(&*self.header_diff_cache)
516 .insert(reference.to_string(), component);
517 }
518}
519
520impl DiffCache<ResponseDiff> for HttpSchemaDiffContext {
521 fn get_diff(
522 &self,
523 reference: &str,
524 ) -> Option<Arc<DiffResult<ResponseDiff>>> {
525 (*self.response_diff_cache)
526 .borrow()
527 .get(reference)
528 .map(Arc::clone)
529 }
530
531 fn set_diff(
532 &self,
533 reference: &str,
534 component: Arc<DiffResult<ResponseDiff>>,
535 ) {
536 let _ = RefCell::borrow_mut(&*self.response_diff_cache)
537 .insert(reference.to_string(), component);
538 }
539}
540
541impl DiffCache<ExampleDiff> for HttpSchemaDiffContext {
542 fn get_diff(
543 &self,
544 reference: &str,
545 ) -> Option<Arc<DiffResult<ExampleDiff>>> {
546 (*self.example_diff_cache)
547 .borrow()
548 .get(reference)
549 .map(Arc::clone)
550 }
551
552 fn set_diff(
553 &self,
554 reference: &str,
555 component: Arc<DiffResult<ExampleDiff>>,
556 ) {
557 let _ = RefCell::borrow_mut(&*self.example_diff_cache)
558 .insert(reference.to_string(), component);
559 }
560}
561
562impl DiffCache<RequestBodyDiff> for HttpSchemaDiffContext {
563 fn get_diff(
564 &self,
565 reference: &str,
566 ) -> Option<Arc<DiffResult<RequestBodyDiff>>> {
567 (*self.request_body_diff_cache)
568 .borrow()
569 .get(reference)
570 .map(Arc::clone)
571 }
572
573 fn set_diff(
574 &self,
575 reference: &str,
576 component: Arc<DiffResult<RequestBodyDiff>>,
577 ) {
578 let _ = RefCell::borrow_mut(&*self.request_body_diff_cache)
579 .insert(reference.to_string(), component);
580 }
581}
582
583impl DiffCache<SecuritySchemeDiff> for HttpSchemaDiffContext {
584 fn get_diff(
585 &self,
586 reference: &str,
587 ) -> Option<Arc<DiffResult<SecuritySchemeDiff>>> {
588 (*self.security_scheme_diff_cache)
589 .borrow()
590 .get(reference)
591 .map(Arc::clone)
592 }
593
594 fn set_diff(
595 &self,
596 reference: &str,
597 component: Arc<DiffResult<SecuritySchemeDiff>>,
598 ) {
599 let _ = RefCell::borrow_mut(&*self.security_scheme_diff_cache)
600 .insert(reference.to_string(), component);
601 }
602}
603
604impl DiffCache<LinkDiff> for HttpSchemaDiffContext {
605 fn get_diff(&self, reference: &str) -> Option<Arc<DiffResult<LinkDiff>>> {
606 (*self.link_diff_cache)
607 .borrow()
608 .get(reference)
609 .map(Arc::clone)
610 }
611
612 fn set_diff(&self, reference: &str, component: Arc<DiffResult<LinkDiff>>) {
613 let _ = RefCell::borrow_mut(&*self.link_diff_cache)
614 .insert(reference.to_string(), component);
615 }
616}