surrealdb_expr/val/value/
walk.rs1use 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
112#[cfg(test)]
113mod tests {
114
115 use super::*;
116 use crate::expr::Idiom;
117 use crate::syn;
118
119 macro_rules! parse_val {
120 ($input:expr) => {
121 crate::val::convert_public_value_to_internal(syn::value($input).unwrap())
122 };
123 }
124
125 #[test]
126 fn walk_blank() {
127 let idi: Idiom = Default::default();
128 let val = parse_val!("{ test: { other: null, something: 123 } }");
129 let res: Vec<(Idiom, Value)> =
130 vec![(Idiom::default(), parse_val!("{ test: { other: null, something: 123 } }"))];
131 assert_eq!(res, val.walk(&idi));
132 }
133
134 #[test]
135 fn walk_basic() {
136 let idi: Idiom = syn::idiom("test.something").unwrap().into();
137 let val = parse_val!("{ test: { other: null, something: 123 } }");
138 let res: Vec<(Idiom, Value)> =
139 vec![(syn::idiom("test.something").unwrap().into(), Value::from(123))];
140 assert_eq!(res, val.walk(&idi));
141 }
142
143 #[test]
144 fn walk_empty() {
145 let idi: Idiom = syn::idiom("test.missing").unwrap().into();
146 let val = parse_val!("{ test: { other: null, something: 123 } }");
147 let res: Vec<(Idiom, Value)> =
148 vec![(syn::idiom("test.missing").unwrap().into(), Value::None)];
149 assert_eq!(res, val.walk(&idi));
150 }
151
152 #[test]
153 fn walk_empty_object() {
154 let idi: Idiom = syn::idiom("none.something.age").unwrap().into();
155 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
156 let res: Vec<(Idiom, Value)> =
157 vec![(syn::idiom("none.something.age").unwrap().into(), Value::None)];
158 assert_eq!(res, val.walk(&idi));
159 }
160
161 #[test]
162 fn walk_empty_array() {
163 let idi: Idiom = syn::idiom("none.something.*.age").unwrap().into();
164 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
165 let res: Vec<(Idiom, Value)> = vec![];
166 assert_eq!(res, val.walk(&idi));
167 }
168
169 #[test]
170 fn walk_empty_array_index() {
171 let idi: Idiom = syn::idiom("none.something[0].age").unwrap().into();
172 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
173 let res: Vec<(Idiom, Value)> =
174 vec![(syn::idiom("none.something[0].age").unwrap().into(), Value::None)];
175 assert_eq!(res, val.walk(&idi));
176 }
177
178 #[test]
179 fn walk_array() {
180 let idi: Idiom = syn::idiom("test.something").unwrap().into();
181 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
182 let res = vec![(
183 syn::idiom("test.something").unwrap().into(),
184 parse_val!("[{ age: 34 }, { age: 36 }]"),
185 )];
186 assert_eq!(res, val.walk(&idi));
187 }
188
189 #[test]
190 fn walk_array_field() {
191 let idi: Idiom = syn::idiom("test.something[*].age").unwrap().into();
192 let val = parse_val!("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
193 let res: Vec<(Idiom, Value)> = vec![
194 (syn::idiom("test.something[0].age").unwrap().into(), Value::from(34)),
195 (syn::idiom("test.something[1].age").unwrap().into(), Value::from(36)),
196 ];
197 assert_eq!(res, val.walk(&idi));
198 }
199
200 #[test]
201 fn walk_array_field_embedded() {
202 let idi: Idiom = syn::idiom("test.something[*].tags").unwrap().into();
203 let val = parse_val!(
204 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
205 );
206 let res: Vec<(Idiom, Value)> = vec![
207 (
208 syn::idiom("test.something[0].tags").unwrap().into(),
209 parse_val!("['code', 'databases']"),
210 ),
211 (
212 syn::idiom("test.something[1].tags").unwrap().into(),
213 parse_val!("['design', 'operations']"),
214 ),
215 ];
216 assert_eq!(res, val.walk(&idi));
217 }
218
219 #[test]
220 fn walk_array_field_embedded_index() {
221 let idi: Idiom = syn::idiom("test.something[*].tags[1]").unwrap().into();
222 let val = parse_val!(
223 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
224 );
225 let res: Vec<(Idiom, Value)> = vec![
226 (syn::idiom("test.something[0].tags[1]").unwrap().into(), Value::from("databases")),
227 (syn::idiom("test.something[1].tags[1]").unwrap().into(), Value::from("operations")),
228 ];
229 assert_eq!(res, val.walk(&idi));
230 }
231
232 #[test]
233 fn walk_set_field_all() {
234 let idi: Idiom = syn::idiom("test.*").unwrap().into();
235 let val = parse_val!("{ test: { ' hello ', } }");
236 let res: Vec<(Idiom, Value)> =
237 vec![(syn::idiom("test[0]").unwrap().into(), Value::from(" hello "))];
238 assert_eq!(res, val.walk(&idi));
239 }
240
241 #[test]
242 fn walk_array_field_embedded_index_all() {
243 let idi: Idiom = syn::idiom("test.something[*].tags[*]").unwrap().into();
244 let val = parse_val!(
245 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
246 );
247 let res: Vec<(Idiom, Value)> = vec![
248 (syn::idiom("test.something[0].tags[0]").unwrap().into(), Value::from("code")),
249 (syn::idiom("test.something[0].tags[1]").unwrap().into(), Value::from("databases")),
250 (syn::idiom("test.something[1].tags[0]").unwrap().into(), Value::from("design")),
251 (syn::idiom("test.something[1].tags[1]").unwrap().into(), Value::from("operations")),
252 ];
253 assert_eq!(res, val.walk(&idi));
254 }
255}