schemadoc_diff/
path_pointer.rs1use 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}