schemadoc_diff/
context.rs

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}