1use crate::expr::idiom::Idiom;
2use crate::expr::part::Part;
3use crate::expr::{Expr, Literal};
4use crate::val::Value;
5
6impl Value {
7 pub fn every(
14 &self,
15 path: Option<&[Part]>,
16 steps: bool,
17 behavior: ArrayBehaviour,
18 ) -> Vec<Idiom> {
19 let mut accum = if let Some(x) = path {
20 let mut res = x.to_vec();
21 while res.ends_with(&[Part::All]) {
22 res.pop();
23 }
24 res
25 } else {
26 Vec::new()
27 };
28
29 let mut build = Vec::new();
30 match path {
31 Some(path) => self.pick(path)._every(steps, behavior, &mut accum, &mut build),
32 None => self._every(steps, behavior, &mut accum, &mut build),
33 }
34 build
35 }
36
37 fn _every(
39 &self,
40 steps: bool,
41 behavior: ArrayBehaviour,
42 accum: &mut Vec<Part>,
43 build: &mut Vec<Idiom>,
44 ) {
45 match self {
46 Value::Object(v) => {
48 if (steps || v.is_empty()) && !accum.is_empty() {
49 build.push(Idiom(accum.clone()))
50 }
51
52 for (k, v) in v.0.iter() {
53 accum.push(Part::Field(k.clone()));
54 v._every(steps, behavior, accum, build);
55 accum.pop();
56 }
57 }
58 Value::Array(v) => {
60 if !accum.is_empty() {
61 build.push(Idiom(accum.clone()))
62 }
63
64 match behavior {
66 ArrayBehaviour::Full => {
68 for (i, v) in v.iter().enumerate().rev() {
70 accum.push(Part::Value(Expr::Literal(Literal::Integer(i as i64))));
71 v._every(steps, behavior, accum, build);
72 accum.pop();
73 }
74 }
75 ArrayBehaviour::Ignore => {}
77 }
78 }
79 _ => {
81 if !accum.is_empty() {
82 build.push(Idiom(accum.clone()))
83 }
84 }
85 }
86 }
87}
88
89#[derive(Clone, Copy, Debug)]
91pub enum ArrayBehaviour {
92 Ignore,
95 Full,
98}
99
100#[cfg(test)]
101mod tests {
102
103 use surrealdb_types::ToSql;
104
105 use super::*;
106 use crate::syn;
107
108 macro_rules! parse_val {
109 ($input:expr) => {
110 crate::val::convert_public_value_to_internal(syn::value($input).unwrap())
111 };
112 }
113
114 #[test]
115 fn every_empty() {
116 let val = parse_val!("{}");
117 let res: Vec<Idiom> = vec![];
118 assert_eq!(res, val.every(None, false, ArrayBehaviour::Ignore));
119 }
120
121 #[test]
122 fn every_with_empty_objects_arrays() {
123 let val = parse_val!("{ test: {}, status: false, something: {age: 45}, tags: []}");
124 let res: Vec<Idiom> = vec![
125 syn::idiom("something.age").unwrap().into(),
126 syn::idiom("status").unwrap().into(),
127 syn::idiom("tags").unwrap().into(),
128 syn::idiom("test").unwrap().into(),
129 ];
130 assert_eq!(res, val.every(None, false, ArrayBehaviour::Ignore));
131 }
132
133 #[test]
134 fn every_without_array_indexes() {
135 let val = parse_val!(
136 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
137 );
138 let res: Vec<Idiom> = vec![syn::idiom("test.something").unwrap().into()];
139 assert_eq!(res, val.every(None, false, ArrayBehaviour::Ignore));
140 }
141
142 #[test]
143 fn every_including_array_indexes() {
144 let val = parse_val!(
145 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
146 );
147 let res: Vec<Idiom> = vec![
148 syn::idiom("test.something").unwrap().into(),
149 syn::idiom("test.something[1].age").unwrap().into(),
150 syn::idiom("test.something[1].tags").unwrap().into(),
151 syn::idiom("test.something[1].tags[1]").unwrap().into(),
152 syn::idiom("test.something[1].tags[0]").unwrap().into(),
153 syn::idiom("test.something[0].age").unwrap().into(),
154 syn::idiom("test.something[0].tags").unwrap().into(),
155 syn::idiom("test.something[0].tags[1]").unwrap().into(),
156 syn::idiom("test.something[0].tags[0]").unwrap().into(),
157 ];
158 assert_eq!(res, val.every(None, false, ArrayBehaviour::Full));
159 }
160
161 #[test]
162 fn every_including_intermediary_nodes_without_array_indexes() {
163 let val = parse_val!(
164 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
165 );
166 let res: Vec<Idiom> =
167 vec![syn::idiom("test").unwrap().into(), syn::idiom("test.something").unwrap().into()];
168 assert_eq!(res, val.every(None, true, ArrayBehaviour::Ignore));
169 }
170
171 #[test]
172 fn every_including_intermediary_nodes_including_array_indexes() {
173 let val = parse_val!(
174 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
175 );
176 let res: Vec<Idiom> = vec![
177 syn::idiom("test").unwrap().into(),
178 syn::idiom("test.something").unwrap().into(),
179 syn::idiom("test.something[1]").unwrap().into(),
180 syn::idiom("test.something[1].age").unwrap().into(),
181 syn::idiom("test.something[1].tags").unwrap().into(),
182 syn::idiom("test.something[1].tags[1]").unwrap().into(),
183 syn::idiom("test.something[1].tags[0]").unwrap().into(),
184 syn::idiom("test.something[0]").unwrap().into(),
185 syn::idiom("test.something[0].age").unwrap().into(),
186 syn::idiom("test.something[0].tags").unwrap().into(),
187 syn::idiom("test.something[0].tags[1]").unwrap().into(),
188 syn::idiom("test.something[0].tags[0]").unwrap().into(),
189 ];
190 assert_eq!(res, val.every(None, true, ArrayBehaviour::Full));
191 }
192
193 #[test]
194 fn every_given_one_path_part() {
195 let val = parse_val!(
196 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
197 );
198
199 let val =
200 val.every(Some(&Idiom::from(syn::idiom("test").unwrap())), true, ArrayBehaviour::Full);
201 for v in val.iter() {
202 println!("{}", v.to_sql());
203 }
204
205 let res: Vec<Idiom> = vec![
206 syn::idiom("test").unwrap().into(),
207 syn::idiom("test.something").unwrap().into(),
208 syn::idiom("test.something[1]").unwrap().into(),
209 syn::idiom("test.something[1].age").unwrap().into(),
210 syn::idiom("test.something[1].tags").unwrap().into(),
211 syn::idiom("test.something[1].tags[1]").unwrap().into(),
212 syn::idiom("test.something[1].tags[0]").unwrap().into(),
213 syn::idiom("test.something[0]").unwrap().into(),
214 syn::idiom("test.something[0].age").unwrap().into(),
215 syn::idiom("test.something[0].tags").unwrap().into(),
216 syn::idiom("test.something[0].tags[1]").unwrap().into(),
217 syn::idiom("test.something[0].tags[0]").unwrap().into(),
218 ];
219 assert_eq!(res, val,);
220 }
221
222 #[test]
223 fn every_given_two_path_parts() {
224 let val = parse_val!(
225 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
226 );
227 let res: Vec<Idiom> = vec![
228 syn::idiom("test.something").unwrap().into(),
229 syn::idiom("test.something[1]").unwrap().into(),
230 syn::idiom("test.something[1].age").unwrap().into(),
231 syn::idiom("test.something[1].tags").unwrap().into(),
232 syn::idiom("test.something[1].tags[1]").unwrap().into(),
233 syn::idiom("test.something[1].tags[0]").unwrap().into(),
234 syn::idiom("test.something[0]").unwrap().into(),
235 syn::idiom("test.something[0].age").unwrap().into(),
236 syn::idiom("test.something[0].tags").unwrap().into(),
237 syn::idiom("test.something[0].tags[1]").unwrap().into(),
238 syn::idiom("test.something[0].tags[0]").unwrap().into(),
239 ];
240 assert_eq!(
241 res,
242 val.every(
243 Some(&Idiom::from(syn::idiom("test.something").unwrap())),
244 true,
245 ArrayBehaviour::Full
246 )
247 );
248 }
249
250 #[test]
251 fn every_including_intermediary_nodes_including_array_indexes_ending_all() {
252 let val = parse_val!(
253 "{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"
254 );
255 let res: Vec<Idiom> = vec![
256 syn::idiom("test.something").unwrap().into(),
257 syn::idiom("test.something[1]").unwrap().into(),
258 syn::idiom("test.something[1].age").unwrap().into(),
259 syn::idiom("test.something[1].tags").unwrap().into(),
260 syn::idiom("test.something[1].tags[1]").unwrap().into(),
261 syn::idiom("test.something[1].tags[0]").unwrap().into(),
262 syn::idiom("test.something[0]").unwrap().into(),
263 syn::idiom("test.something[0].age").unwrap().into(),
264 syn::idiom("test.something[0].tags").unwrap().into(),
265 syn::idiom("test.something[0].tags[1]").unwrap().into(),
266 syn::idiom("test.something[0].tags[0]").unwrap().into(),
267 ];
268 assert_eq!(
269 res,
270 val.every(
271 Some(&Idiom::from(syn::idiom("test.something.*").unwrap())),
272 true,
273 ArrayBehaviour::Full
274 )
275 );
276 }
277
278 #[test]
279 fn every_wildcards() {
280 let val = parse_val!(
281 "{ test: { a: { color: 'red' }, b: { color: 'blue' }, c: { color: 'green' } } }"
282 );
283
284 let res: Vec<Idiom> = vec![
285 syn::idiom("test.*.color").unwrap().into(),
286 syn::idiom("test.*.color[2]").unwrap().into(),
287 syn::idiom("test.*.color[1]").unwrap().into(),
288 syn::idiom("test.*.color[0]").unwrap().into(),
289 ];
290
291 assert_eq!(
292 res,
293 val.every(
294 Some(&Idiom::from(syn::idiom("test.*.color").unwrap())),
295 true,
296 ArrayBehaviour::Full
297 )
298 );
299 }
300}