schemadoc_diff/
path_pointer.rs

1use crate::diff_result_type::DiffResultType;
2
3#[derive(Debug, Clone)]
4pub enum PointerAncestor {
5    Scope(PathPointerScope),
6    Relative(usize),
7}
8
9impl PointerAncestor {
10    pub fn parent() -> Self {
11        Self::Relative(1)
12    }
13
14    pub fn path() -> Self {
15        Self::Scope(PathPointerScope::Path)
16    }
17
18    pub fn operation() -> Self {
19        Self::Scope(PathPointerScope::Operation)
20    }
21
22    pub fn schema() -> Self {
23        Self::Scope(PathPointerScope::Schema)
24    }
25
26    pub fn schema_property() -> Self {
27        Self::Scope(PathPointerScope::SchemaProperty)
28    }
29
30    pub fn schema_properties() -> Self {
31        Self::Scope(PathPointerScope::SchemaProperties)
32    }
33}
34
35#[derive(Debug, Clone, PartialEq)]
36pub enum PathPointerScope {
37    Paths,
38    Path,
39    Operation,
40
41    RequestBody,
42    Responses,
43    ResponseCode,
44    Parameters,
45
46    MediaType,
47
48    Schema,
49    SchemaProperties,
50    SchemaProperty,
51
52    SchemaItems,
53    SchemaNot,
54
55    SchemaAllOf,
56    SchemaAnyOf,
57    SchemaOneOf,
58    SchemaAdditionalProperties,
59}
60
61#[derive(Debug, Clone, PartialEq)]
62pub struct PathPointerComponent {
63    pub kind: DiffResultType,
64    pub path: Option<String>,
65    pub scope: Option<PathPointerScope>,
66}
67
68#[derive(Debug, Clone, PartialEq)]
69pub struct PathPointer {
70    pub components: Vec<PathPointerComponent>,
71}
72
73impl PathPointer {
74    pub fn new<S: Into<String>, C: Into<DiffResultType>>(
75        context: C,
76        path: Option<S>,
77        scope: Option<PathPointerScope>,
78    ) -> Self {
79        Self {
80            components: vec![PathPointerComponent {
81                scope,
82                kind: context.into(),
83                path: path.map(|path| path.into()),
84            }],
85        }
86    }
87
88    pub fn add<S: Into<String>, C: Into<DiffResultType>>(
89        &self,
90        context: C,
91        path: S,
92        scope: Option<PathPointerScope>,
93    ) -> Self {
94        self.add_component(context, Some(path), scope)
95    }
96
97    pub fn add_context<C: Into<DiffResultType>>(&self, context: C) -> Self {
98        self.add_component::<String, C>(context, None, None)
99    }
100
101    pub fn add_component<S: Into<String>, C: Into<DiffResultType>>(
102        &self,
103        context: C,
104        path: Option<S>,
105        scope: Option<PathPointerScope>,
106    ) -> Self {
107        let mut new = self.clone();
108        new.components.push(PathPointerComponent {
109            scope,
110            kind: context.into(),
111            path: path.map(|path| path.into()),
112        });
113        new
114    }
115
116    pub fn get(
117        &self,
118        scope: PathPointerScope,
119    ) -> Option<&PathPointerComponent> {
120        self.components
121            .iter()
122            .find(|c| c.scope.as_ref().map_or(false, |v| v == &scope))
123    }
124
125    pub fn is_in(&self, scope: PathPointerScope) -> bool {
126        self.get(scope).is_some()
127    }
128
129    pub fn this(&self) -> DiffResultType {
130        self.get_primary(None)
131    }
132
133    pub fn parent(&self) -> DiffResultType {
134        self.get_primary(Some(PointerAncestor::parent()))
135    }
136
137    pub fn ancestor(&self, ancestor: PointerAncestor) -> DiffResultType {
138        self.get_primary(Some(ancestor))
139    }
140
141    fn get_primary(
142        &self,
143        ancestor: Option<PointerAncestor>,
144    ) -> DiffResultType {
145        let mut found = true;
146        let mut result = DiffResultType::None;
147
148        if let Some(ancestor) = ancestor {
149            match ancestor {
150                PointerAncestor::Scope(ref target_scope) => {
151                    let latest = self
152                        .components
153                        .iter()
154                        .rfind(|c| c.scope.as_ref() == Some(target_scope));
155                    found = match latest {
156                        Some(latest) => {
157                            let mut is_latest_found = false;
158                            let iterator =
159                                self.components.iter().take_while(|c| {
160                                    if is_latest_found {
161                                        return false;
162                                    }
163                                    is_latest_found |= *c == latest;
164                                    true
165                                });
166
167                            for PathPointerComponent { kind, .. } in iterator {
168                                if kind.is_added() | kind.is_removed() {
169                                    return *kind;
170                                } else if kind.is_same() || kind.is_updated() {
171                                    result = *kind;
172                                }
173                            }
174                            true
175                        }
176                        None => false,
177                    };
178                }
179                PointerAncestor::Relative(value) => {
180                    let slice =
181                        &self.components[..(self.components.len() - value)];
182                    for PathPointerComponent { kind, .. } in slice {
183                        if kind.is_added() | kind.is_removed() {
184                            return *kind;
185                        } else if kind.is_same() || kind.is_updated() {
186                            result = *kind;
187                        }
188                    }
189                }
190            }
191        } else {
192            for PathPointerComponent { kind, .. } in self.components.iter() {
193                if kind.is_added() | kind.is_removed() {
194                    return *kind;
195                } else if kind.is_same() || kind.is_updated() {
196                    result = *kind;
197                }
198            }
199        };
200
201        if found {
202            result
203        } else {
204            DiffResultType::None
205        }
206    }
207
208    pub fn is_same(&self) -> bool {
209        self.this().is_same()
210    }
211
212    pub fn is_added(&self) -> bool {
213        self.this().is_added()
214    }
215
216    pub fn is_updated(&self) -> bool {
217        self.this().is_updated()
218    }
219
220    pub fn is_upserted(&self) -> bool {
221        self.this().is_upserted()
222    }
223
224    pub fn is_removed(&self) -> bool {
225        self.this().is_removed()
226    }
227
228    pub fn get_path(&self) -> String {
229        self.components
230            .iter()
231            .filter_map(|c| c.path.clone())
232            .collect::<Vec<String>>()
233            .join("/")
234    }
235
236    pub fn startswith(&self, value: &PathPointer) -> bool {
237        self.get_path().starts_with(&value.get_path())
238    }
239
240    pub fn matches(&self, value: &str) -> bool {
241        if value == "*" {
242            true
243        } else {
244            value.trim_matches('/') == self.get_path().trim_matches('/')
245        }
246    }
247}