schemadoc_diff/visitors/
affected_operations_visitor.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3
4use crate::core::{DiffResult, MapDiff, VecDiff};
5use crate::diff_result_type::DiffResultType;
6use crate::path_pointer::PathPointer;
7use crate::schema_diff::{
8    deref_parameter_diff, deref_request_body_diff, deref_response_diff,
9    deref_schema_diff, HttpSchemaDiff, MayBeRefDiff, MediaTypeDiff,
10    OperationDiff, ParameterDiff, RequestBodyDiff, ResponseDiff, SchemaDiff,
11};
12
13use crate::visitor::{dispatch_visitor, DiffVisitor};
14
15#[derive(Eq, Hash, PartialEq, Debug)]
16pub enum SharedChangeComponent {
17    RequestBody,
18    Parameter,
19    Response,
20    Schema,
21}
22
23#[derive(Eq, Hash, PartialEq, Debug)]
24pub struct SharedChange {
25    kind: DiffResultType,
26    reference: String,
27    component: SharedChangeComponent,
28}
29
30struct SharedChangesVisitor<'s> {
31    diff: &'s HttpSchemaDiff,
32    pointers: RefCell<HashMap<SharedChange, Vec<PathPointer>>>,
33}
34
35impl<'s> SharedChangesVisitor<'s> {
36    pub fn new(diff: &'s HttpSchemaDiff) -> Self {
37        Self {
38            diff,
39            pointers: RefCell::new(HashMap::new()),
40        }
41    }
42}
43
44impl<'s> DiffVisitor<'s> for SharedChangesVisitor<'s> {
45    /// Actual code
46
47    fn visit_schema_ref(
48        &self,
49        pointer: &PathPointer,
50        may_be_ref: &'s DiffResult<MayBeRefDiff<SchemaDiff>>,
51    ) -> bool {
52        if !pointer.parent().is_updated() || may_be_ref.is_same_or_none() {
53            return false;
54        }
55
56        let Some(value) = may_be_ref.get() else {
57            return false;
58        };
59
60        let MayBeRefDiff::Ref(reference) = value else {
61            return true;
62        };
63
64        let Some(diff) = deref_schema_diff(self.diff, value) else {
65            return false;
66        };
67
68        let key = SharedChange {
69            kind: diff.into(),
70            reference: reference.reference.clone(),
71            component: SharedChangeComponent::Schema,
72        };
73
74        self.pointers
75            .borrow_mut()
76            .entry(key)
77            .and_modify(|arr| arr.push(pointer.clone()))
78            .or_insert_with(|| vec![pointer.clone()]);
79
80        false
81    }
82
83    fn visit_response_ref(
84        &self,
85        pointer: &PathPointer,
86        may_be_ref: &'s DiffResult<MayBeRefDiff<ResponseDiff>>,
87    ) -> bool {
88        if !pointer.parent().is_updated() || may_be_ref.is_same_or_none() {
89            return false;
90        }
91
92        let Some(value) = may_be_ref.get() else {
93            return false;
94        };
95
96        let MayBeRefDiff::Ref(reference) = value else {
97            return true;
98        };
99
100        let Some(diff) = deref_response_diff(self.diff, value) else {
101            return false;
102        };
103
104        let key = SharedChange {
105            kind: diff.into(),
106            reference: reference.reference.clone(),
107            component: SharedChangeComponent::Response,
108        };
109
110        self.pointers
111            .borrow_mut()
112            .entry(key)
113            .and_modify(|arr| arr.push(pointer.clone()))
114            .or_insert_with(|| vec![pointer.clone()]);
115
116        false
117    }
118
119    fn visit_parameter_ref(
120        &self,
121        pointer: &PathPointer,
122        may_be_ref: &'s DiffResult<MayBeRefDiff<ParameterDiff>>,
123    ) -> bool {
124        if !pointer.parent().is_updated() || may_be_ref.is_same_or_none() {
125            return false;
126        }
127
128        let Some(value) = may_be_ref.get() else {
129            return false;
130        };
131
132        let MayBeRefDiff::Ref(reference) = value else {
133            return true;
134        };
135
136        let Some(diff) = deref_parameter_diff(self.diff, value) else {
137            return false;
138        };
139
140        let key = SharedChange {
141            kind: diff.into(),
142            reference: reference.reference.clone(),
143            component: SharedChangeComponent::Parameter,
144        };
145
146        self.pointers
147            .borrow_mut()
148            .entry(key)
149            .and_modify(|arr| arr.push(pointer.clone()))
150            .or_insert_with(|| vec![pointer.clone()]);
151
152        false
153    }
154
155    fn visit_request_body_ref(
156        &self,
157        pointer: &PathPointer,
158        may_be_ref: &'s DiffResult<MayBeRefDiff<RequestBodyDiff>>,
159    ) -> bool {
160        if !pointer.parent().is_updated() || may_be_ref.is_same_or_none() {
161            return false;
162        }
163
164        let Some(value) = may_be_ref.get() else {
165            return false;
166        };
167
168        let MayBeRefDiff::Ref(reference) = value else {
169            return true;
170        };
171
172        let Some(diff) = deref_request_body_diff(self.diff, value) else {
173            return false;
174        };
175
176        let key = SharedChange {
177            kind: diff.into(),
178            reference: reference.reference.clone(),
179            component: SharedChangeComponent::RequestBody,
180        };
181
182        self.pointers
183            .borrow_mut()
184            .entry(key)
185            .and_modify(|arr| arr.push(pointer.clone()))
186            .or_insert_with(|| vec![pointer.clone()]);
187
188        false
189    }
190    fn visit_operation(
191        &self,
192        p: &PathPointer,
193        _: &str,
194        _: &'s DiffResult<OperationDiff>,
195    ) -> bool {
196        p.parent().is_updated()
197    }
198
199    fn visit_request_body(
200        &self,
201        p: &PathPointer,
202        _: &'s DiffResult<RequestBodyDiff>,
203    ) -> bool {
204        p.parent().is_updated()
205    }
206
207    fn visit_responses(
208        &self,
209        p: &PathPointer,
210        _: &'s DiffResult<MapDiff<MayBeRefDiff<ResponseDiff>>>,
211    ) -> bool {
212        p.parent().is_updated()
213    }
214
215    fn visit_media_types(
216        &self,
217        p: &PathPointer,
218        _: &'s DiffResult<MapDiff<MediaTypeDiff>>,
219    ) -> bool {
220        p.parent().is_updated()
221    }
222
223    fn visit_media_type(
224        &self,
225        p: &PathPointer,
226        _: &'s DiffResult<MediaTypeDiff>,
227    ) -> bool {
228        p.parent().is_updated()
229    }
230
231    fn visit_parameters(
232        &self,
233        p: &PathPointer,
234        _: &'s DiffResult<VecDiff<MayBeRefDiff<ParameterDiff>>>,
235    ) -> bool {
236        p.parent().is_updated()
237    }
238
239    fn visit_parameter(
240        &self,
241        p: &PathPointer,
242        _: &'s DiffResult<ParameterDiff>,
243    ) -> bool {
244        p.parent().is_updated()
245    }
246
247    fn visit_schema(
248        &self,
249        p: &PathPointer,
250        _: &'s DiffResult<SchemaDiff>,
251    ) -> bool {
252        p.parent().is_updated()
253    }
254}
255
256pub fn get_shared_changes(
257    diff: &HttpSchemaDiff,
258) -> HashMap<SharedChange, Vec<PathPointer>> {
259    let visitor = SharedChangesVisitor::new(diff);
260
261    dispatch_visitor(diff, &visitor);
262
263    visitor.pointers.into_inner()
264}