1use crate::expr::idiom::Idiom;
2use crate::expr::part::{Next, Part};
3use crate::val::Value;
4
5impl Value {
6 pub fn walk(&self, path: &[Part]) -> Vec<(Idiom, Self)> {
7 self._walk(path, Idiom::default())
8 }
9 fn _walk(&self, path: &[Part], prev: Idiom) -> Vec<(Idiom, Self)> {
10 match path.first() {
11 Some(p) => match self {
13 Value::Object(v) => match p {
15 Part::Field(f) => match v.get(f as &str) {
16 Some(v) => v._walk(path.next(), prev.push(p.clone())),
17 None => Value::None._walk(path.next(), prev.push(p.clone())),
18 },
19 Part::All => v
20 .iter()
21 .flat_map(|(field, v)| {
22 v._walk(path.next(), prev.clone().push(Part::Field(field.clone())))
23 })
24 .collect::<Vec<_>>(),
25 x => {
26 if let Some(idx) = x.as_old_index() {
27 match v.get(&idx.to_string()) {
28 Some(v) => v._walk(path.next(), prev.push(p.clone())),
29 None => Value::None._walk(path.next(), prev.push(p.clone())),
30 }
31 } else {
32 vec![]
33 }
34 }
35 },
36 Value::Array(v) => match p {
38 Part::First => match v.first() {
39 Some(v) => v._walk(path.next(), prev.push(p.clone())),
40 None => vec![],
41 },
42 Part::Last => match v.last() {
43 Some(v) => v._walk(path.next(), prev.push(p.clone())),
44 None => vec![],
45 },
46 x => {
47 if let Some(idx) = x.as_old_index() {
48 match v.get(idx) {
49 Some(v) => v._walk(path.next(), prev.push(p.clone())),
50 None => vec![],
51 }
52 } else {
53 v.iter()
54 .enumerate()
55 .flat_map(|(i, v)| {
56 v._walk(
57 path.next(),
58 prev.clone().push(Part::index_int(i as i64)),
59 )
60 })
61 .collect::<Vec<_>>()
62 }
63 }
64 },
65 Value::Set(v) => match p {
67 Part::First => match v.first() {
68 Some(v) => v._walk(path.next(), prev.push(p.clone())),
69 None => vec![],
70 },
71 Part::Last => match v.last() {
72 Some(v) => v._walk(path.next(), prev.push(p.clone())),
73 None => vec![],
74 },
75 x => {
76 if let Some(idx) = x.as_old_index() {
77 match v.nth(idx) {
78 Some(v) => v._walk(path.next(), prev.push(p.clone())),
79 None => vec![],
80 }
81 } else {
82 v.iter()
83 .enumerate()
84 .flat_map(|(i, v)| {
85 v._walk(
86 path.next(),
87 prev.clone().push(Part::index_int(i as i64)),
88 )
89 })
90 .collect::<Vec<_>>()
91 }
92 }
93 },
94 _ => match p {
96 Part::Field(_) => Value::None._walk(path.next(), prev.push(p.clone())),
97 x => {
98 if x.as_old_index().is_some() {
99 Value::None._walk(path.next(), prev.push(p.clone()))
100 } else {
101 vec![]
102 }
103 }
104 },
105 },
106 None => vec![(prev, self.clone())],
108 }
109 }
110
111 pub fn walk_ref(&self, path: &[Part], visit: &mut impl FnMut(&Self)) {
121 let Some(p) = path.first() else {
122 visit(self);
124 return;
125 };
126 match self {
127 Value::Object(v) => match p {
129 Part::Field(f) => match v.get(f as &str) {
130 Some(v) => v.walk_ref(path.next(), visit),
131 None => Value::None.walk_ref(path.next(), visit),
132 },
133 Part::All => {
134 for (_, v) in v.iter() {
135 v.walk_ref(path.next(), visit);
136 }
137 }
138 x => {
139 if let Some(idx) = x.as_old_index() {
140 match v.get(&idx.to_string()) {
141 Some(v) => v.walk_ref(path.next(), visit),
142 None => Value::None.walk_ref(path.next(), visit),
143 }
144 }
145 }
146 },
147 Value::Array(v) => match p {
149 Part::First => {
150 if let Some(v) = v.first() {
151 v.walk_ref(path.next(), visit);
152 }
153 }
154 Part::Last => {
155 if let Some(v) = v.last() {
156 v.walk_ref(path.next(), visit);
157 }
158 }
159 x => match x.as_old_index() {
160 Some(idx) => {
161 if let Some(v) = v.get(idx) {
162 v.walk_ref(path.next(), visit);
163 }
164 }
165 None => {
166 for v in v.iter() {
167 v.walk_ref(path.next(), visit);
168 }
169 }
170 },
171 },
172 Value::Set(v) => match p {
174 Part::First => {
175 if let Some(v) = v.first() {
176 v.walk_ref(path.next(), visit);
177 }
178 }
179 Part::Last => {
180 if let Some(v) = v.last() {
181 v.walk_ref(path.next(), visit);
182 }
183 }
184 x => match x.as_old_index() {
185 Some(idx) => {
186 if let Some(v) = v.nth(idx) {
187 v.walk_ref(path.next(), visit);
188 }
189 }
190 None => {
191 for v in v.iter() {
192 v.walk_ref(path.next(), visit);
193 }
194 }
195 },
196 },
197 _ => match p {
199 Part::Field(_) => Value::None.walk_ref(path.next(), visit),
200 x => {
201 if x.as_old_index().is_some() {
202 Value::None.walk_ref(path.next(), visit);
203 }
204 }
205 },
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212
213 use super::*;
214 use crate::expr::Idiom;
215 use crate::syn;
216
217 macro_rules! parse_val {
218 ($input:expr) => {
219 crate::val::convert_public_value_to_internal(syn::value($input).unwrap())
220 };
221 }
222
223 #[test]
224 fn walk_blank() {
225 let idi: Idiom = Default::default();
226 let val = parse_val!("{ test: { other: null, something: 123 } }");
227 let res: Vec<(Idiom, Value)> =
228 vec![(Idiom::default(), parse_val!("{ test: { other: null, something: 123 } }"))];
229 assert_eq!(res, val.walk(&idi));
230 }
231
232 #[test]
233 fn walk_basic() {
234 let idi: Idiom = syn::idiom("test.something").unwrap().into();
235 let val = parse_val!("{ test: { other: null, something: 123 } }");
236 let res: Vec<(Idiom, Value)> =
237 vec![(syn::idiom("test.something").unwrap().into(), Value::from(123))];
238 assert_eq!(res, val.walk(&idi));
239 }
240
241 #[test]
242 fn walk_empty() {
243 let idi: Idiom = syn::idiom("test.missing").unwrap().into();
244 let val = parse_val!("{ test: { other: null, something: 123 } }");
245 let res: Vec<(Idiom, Value)> =
246 vec![(syn::idiom("test.missing").unwrap().into(), Value::None)];
247 assert_eq!(res, val.walk(&idi));
248 }
249
250 #[test]
251 fn walk_empty_object() {
252 let idi: Idiom = syn::idiom("none.something.age").unwrap().into();
253 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
254 let res: Vec<(Idiom, Value)> =
255 vec![(syn::idiom("none.something.age").unwrap().into(), Value::None)];
256 assert_eq!(res, val.walk(&idi));
257 }
258
259 #[test]
260 fn walk_empty_array() {
261 let idi: Idiom = syn::idiom("none.something.*.age").unwrap().into();
262 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
263 let res: Vec<(Idiom, Value)> = vec![];
264 assert_eq!(res, val.walk(&idi));
265 }
266
267 #[test]
268 fn walk_empty_array_index() {
269 let idi: Idiom = syn::idiom("none.something[0].age").unwrap().into();
270 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
271 let res: Vec<(Idiom, Value)> =
272 vec![(syn::idiom("none.something[0].age").unwrap().into(), Value::None)];
273 assert_eq!(res, val.walk(&idi));
274 }
275
276 #[test]
277 fn walk_array() {
278 let idi: Idiom = syn::idiom("test.something").unwrap().into();
279 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
280 let res = vec![(
281 syn::idiom("test.something").unwrap().into(),
282 parse_val!("[{ age: 34 }, { age: 36 }]"),
283 )];
284 assert_eq!(res, val.walk(&idi));
285 }
286
287 #[test]
288 fn walk_array_field() {
289 let idi: Idiom = syn::idiom("test.something[*].age").unwrap().into();
290 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
291 let res: Vec<(Idiom, Value)> = vec![
292 (syn::idiom("test.something[0].age").unwrap().into(), Value::from(34)),
293 (syn::idiom("test.something[1].age").unwrap().into(), Value::from(36)),
294 ];
295 assert_eq!(res, val.walk(&idi));
296 }
297
298 #[test]
299 fn walk_array_field_embedded() {
300 let idi: Idiom = syn::idiom("test.something[*].tags").unwrap().into();
301 let val = parse_val!(
302 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
303 );
304 let res: Vec<(Idiom, Value)> = vec![
305 (
306 syn::idiom("test.something[0].tags").unwrap().into(),
307 parse_val!("['code', 'databases']"),
308 ),
309 (
310 syn::idiom("test.something[1].tags").unwrap().into(),
311 parse_val!("['design', 'operations']"),
312 ),
313 ];
314 assert_eq!(res, val.walk(&idi));
315 }
316
317 #[test]
318 fn walk_array_field_embedded_index() {
319 let idi: Idiom = syn::idiom("test.something[*].tags[1]").unwrap().into();
320 let val = parse_val!(
321 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
322 );
323 let res: Vec<(Idiom, Value)> = vec![
324 (syn::idiom("test.something[0].tags[1]").unwrap().into(), Value::from("databases")),
325 (syn::idiom("test.something[1].tags[1]").unwrap().into(), Value::from("operations")),
326 ];
327 assert_eq!(res, val.walk(&idi));
328 }
329
330 #[test]
331 fn walk_set_field_all() {
332 let idi: Idiom = syn::idiom("test.*").unwrap().into();
333 let val = parse_val!("{ test: { ' hello ', } }");
334 let res: Vec<(Idiom, Value)> =
335 vec![(syn::idiom("test[0]").unwrap().into(), Value::from(" hello "))];
336 assert_eq!(res, val.walk(&idi));
337 }
338
339 #[test]
340 fn walk_array_field_embedded_index_all() {
341 let idi: Idiom = syn::idiom("test.something[*].tags[*]").unwrap().into();
342 let val = parse_val!(
343 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
344 );
345 let res: Vec<(Idiom, Value)> = vec![
346 (syn::idiom("test.something[0].tags[0]").unwrap().into(), Value::from("code")),
347 (syn::idiom("test.something[0].tags[1]").unwrap().into(), Value::from("databases")),
348 (syn::idiom("test.something[1].tags[0]").unwrap().into(), Value::from("design")),
349 (syn::idiom("test.something[1].tags[1]").unwrap().into(), Value::from("operations")),
350 ];
351 assert_eq!(res, val.walk(&idi));
352 }
353
354 #[test]
358 fn walk_ref_visits_what_walk_returns() {
359 let val = parse_val!(
360 "{ test: { other: null, something: [{ age: 34, tags: ['code', 'databases'] }, \
361 { age: 36, tags: [] }] }, tags: ['a', 'b', 'c'], empty: [], set: { 'x', 'y', }, n: 1 }"
362 );
363 for path in [
364 "test",
365 "test.something",
366 "test.missing",
367 "test.*",
368 "test.something[*].age",
369 "test.something[0].tags[1]",
370 "test.something[*].tags[*]",
371 "tags",
372 "tags[*]",
373 "tags[1]",
374 "tags[5]",
375 "empty",
376 "empty[*]",
377 "empty[0]",
378 "set",
379 "set[*]",
380 "set[0]",
381 "n",
382 "n.missing",
383 "missing.deep[*]",
384 ] {
385 let idi: Idiom = syn::idiom(path).unwrap().into();
386 let mut visited = Vec::new();
387 val.walk_ref(&idi, &mut |v| visited.push(v.clone()));
388 let walked: Vec<Value> = val.walk(&idi).into_iter().map(|(_, v)| v).collect();
389 assert_eq!(visited, walked, "the two walks must agree on `{path}`");
390 }
391 }
392}