1use crate::error::DkitError;
2use crate::query::parser::{Path, Segment};
3use crate::value::Value;
4
5pub fn evaluate_path(value: &Value, path: &Path) -> Result<Value, DkitError> {
7 let mut results = vec![value.clone()];
8
9 for segment in &path.segments {
10 let mut next_results = Vec::new();
11
12 for val in &results {
13 match segment {
14 Segment::Field(name) => match val {
15 Value::Object(map) => match map.get(name) {
16 Some(v) => next_results.push(v.clone()),
17 None => {
18 return Err(DkitError::PathNotFound(format!(
19 "field '{}' not found",
20 name
21 )));
22 }
23 },
24 _ => {
25 return Err(DkitError::PathNotFound(format!(
26 "cannot access field '{}' on non-object value",
27 name
28 )));
29 }
30 },
31 Segment::Index(idx) => match val {
32 Value::Array(arr) => {
33 let resolved = resolve_index(*idx, arr.len())?;
34 next_results.push(arr[resolved].clone());
35 }
36 _ => {
37 return Err(DkitError::PathNotFound(format!(
38 "cannot index non-array value with [{}]",
39 idx
40 )));
41 }
42 },
43 Segment::Iterate => match val {
44 Value::Array(arr) => {
45 next_results.extend(arr.iter().cloned());
46 }
47 _ => {
48 return Err(DkitError::PathNotFound(
49 "cannot iterate over non-array value".to_string(),
50 ));
51 }
52 },
53 }
54 }
55
56 results = next_results;
57 }
58
59 let has_iterate = path.segments.iter().any(|s| matches!(s, Segment::Iterate));
61 if has_iterate {
62 Ok(Value::Array(results))
63 } else {
64 match results.len() {
66 0 => Err(DkitError::PathNotFound("empty result".to_string())),
67 1 => Ok(results.into_iter().next().unwrap()),
68 _ => Ok(Value::Array(results)),
69 }
70 }
71}
72
73fn resolve_index(index: i64, len: usize) -> Result<usize, DkitError> {
75 let resolved = if index < 0 {
76 let positive = (-index) as usize;
77 if positive > len {
78 return Err(DkitError::PathNotFound(format!(
79 "index {} out of bounds (array length: {})",
80 index, len
81 )));
82 }
83 len - positive
84 } else {
85 let idx = index as usize;
86 if idx >= len {
87 return Err(DkitError::PathNotFound(format!(
88 "index {} out of bounds (array length: {})",
89 index, len
90 )));
91 }
92 idx
93 };
94 Ok(resolved)
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::query::parser::parse_query;
101 use indexmap::IndexMap;
102
103 fn eval(value: &Value, query_str: &str) -> Result<Value, DkitError> {
104 let query = parse_query(query_str).unwrap();
105 evaluate_path(value, &query.path)
106 }
107
108 fn sample_data() -> Value {
109 let mut data = IndexMap::new();
120 data.insert("name".to_string(), Value::String("dkit".to_string()));
121 data.insert("version".to_string(), Value::Integer(1));
122
123 let users = vec![
124 {
125 let mut u = IndexMap::new();
126 u.insert("name".to_string(), Value::String("Alice".to_string()));
127 u.insert("age".to_string(), Value::Integer(30));
128 Value::Object(u)
129 },
130 {
131 let mut u = IndexMap::new();
132 u.insert("name".to_string(), Value::String("Bob".to_string()));
133 u.insert("age".to_string(), Value::Integer(25));
134 Value::Object(u)
135 },
136 {
137 let mut u = IndexMap::new();
138 u.insert("name".to_string(), Value::String("Charlie".to_string()));
139 u.insert("age".to_string(), Value::Integer(35));
140 Value::Object(u)
141 },
142 ];
143 data.insert("users".to_string(), Value::Array(users));
144
145 let mut db = IndexMap::new();
146 db.insert("host".to_string(), Value::String("localhost".to_string()));
147 db.insert("port".to_string(), Value::Integer(5432));
148 let mut config = IndexMap::new();
149 config.insert("database".to_string(), Value::Object(db));
150 data.insert("config".to_string(), Value::Object(config));
151
152 Value::Object(data)
153 }
154
155 #[test]
158 fn test_root() {
159 let data = sample_data();
160 let result = eval(&data, ".").unwrap();
161 assert_eq!(result, data);
162 }
163
164 #[test]
167 fn test_field_access() {
168 let data = sample_data();
169 let result = eval(&data, ".name").unwrap();
170 assert_eq!(result, Value::String("dkit".to_string()));
171 }
172
173 #[test]
174 fn test_nested_field() {
175 let data = sample_data();
176 let result = eval(&data, ".config.database.host").unwrap();
177 assert_eq!(result, Value::String("localhost".to_string()));
178 }
179
180 #[test]
181 fn test_nested_field_integer() {
182 let data = sample_data();
183 let result = eval(&data, ".config.database.port").unwrap();
184 assert_eq!(result, Value::Integer(5432));
185 }
186
187 #[test]
188 fn test_field_not_found() {
189 let data = sample_data();
190 let err = eval(&data, ".nonexistent").unwrap_err();
191 assert!(matches!(err, DkitError::PathNotFound(_)));
192 }
193
194 #[test]
195 fn test_field_on_non_object() {
196 let data = sample_data();
197 let err = eval(&data, ".name.sub").unwrap_err();
198 assert!(matches!(err, DkitError::PathNotFound(_)));
199 }
200
201 #[test]
204 fn test_array_index_zero() {
205 let data = sample_data();
206 let result = eval(&data, ".users[0]").unwrap();
207 let obj = result.as_object().unwrap();
208 assert_eq!(obj.get("name"), Some(&Value::String("Alice".to_string())));
209 }
210
211 #[test]
212 fn test_array_index_last() {
213 let data = sample_data();
214 let result = eval(&data, ".users[-1]").unwrap();
215 let obj = result.as_object().unwrap();
216 assert_eq!(obj.get("name"), Some(&Value::String("Charlie".to_string())));
217 }
218
219 #[test]
220 fn test_array_index_with_field() {
221 let data = sample_data();
222 let result = eval(&data, ".users[0].name").unwrap();
223 assert_eq!(result, Value::String("Alice".to_string()));
224 }
225
226 #[test]
227 fn test_array_index_negative_two() {
228 let data = sample_data();
229 let result = eval(&data, ".users[-2].name").unwrap();
230 assert_eq!(result, Value::String("Bob".to_string()));
231 }
232
233 #[test]
234 fn test_array_index_out_of_bounds() {
235 let data = sample_data();
236 let err = eval(&data, ".users[10]").unwrap_err();
237 assert!(matches!(err, DkitError::PathNotFound(_)));
238 }
239
240 #[test]
241 fn test_array_index_negative_out_of_bounds() {
242 let data = sample_data();
243 let err = eval(&data, ".users[-10]").unwrap_err();
244 assert!(matches!(err, DkitError::PathNotFound(_)));
245 }
246
247 #[test]
248 fn test_index_on_non_array() {
249 let data = sample_data();
250 let err = eval(&data, ".name[0]").unwrap_err();
251 assert!(matches!(err, DkitError::PathNotFound(_)));
252 }
253
254 #[test]
257 fn test_iterate() {
258 let data = sample_data();
259 let result = eval(&data, ".users[]").unwrap();
260 let arr = result.as_array().unwrap();
261 assert_eq!(arr.len(), 3);
262 }
263
264 #[test]
265 fn test_iterate_with_field() {
266 let data = sample_data();
267 let result = eval(&data, ".users[].name").unwrap();
268 assert_eq!(
269 result,
270 Value::Array(vec![
271 Value::String("Alice".to_string()),
272 Value::String("Bob".to_string()),
273 Value::String("Charlie".to_string()),
274 ])
275 );
276 }
277
278 #[test]
279 fn test_iterate_with_field_integer() {
280 let data = sample_data();
281 let result = eval(&data, ".users[].age").unwrap();
282 assert_eq!(
283 result,
284 Value::Array(vec![
285 Value::Integer(30),
286 Value::Integer(25),
287 Value::Integer(35),
288 ])
289 );
290 }
291
292 #[test]
293 fn test_iterate_empty_array() {
294 let data = Value::Object({
295 let mut m = IndexMap::new();
296 m.insert("items".to_string(), Value::Array(vec![]));
297 m
298 });
299 let result = eval(&data, ".items[]").unwrap();
300 assert_eq!(result, Value::Array(vec![]));
301 }
302
303 #[test]
304 fn test_iterate_on_non_array() {
305 let data = sample_data();
306 let err = eval(&data, ".name[]").unwrap_err();
307 assert!(matches!(err, DkitError::PathNotFound(_)));
308 }
309
310 #[test]
313 fn test_root_array_index() {
314 let data = Value::Array(vec![
315 Value::Integer(10),
316 Value::Integer(20),
317 Value::Integer(30),
318 ]);
319 let result = eval(&data, ".[0]").unwrap();
320 assert_eq!(result, Value::Integer(10));
321 }
322
323 #[test]
324 fn test_root_array_iterate() {
325 let data = Value::Array(vec![
326 Value::Integer(10),
327 Value::Integer(20),
328 Value::Integer(30),
329 ]);
330 let result = eval(&data, ".[]").unwrap();
331 assert_eq!(result, data);
332 }
333
334 #[test]
337 fn test_nested_iterate() {
338 let data = Value::Object({
339 let mut m = IndexMap::new();
340 m.insert(
341 "groups".to_string(),
342 Value::Array(vec![
343 Value::Object({
344 let mut g = IndexMap::new();
345 g.insert(
346 "members".to_string(),
347 Value::Array(vec![
348 Value::String("a".to_string()),
349 Value::String("b".to_string()),
350 ]),
351 );
352 g
353 }),
354 Value::Object({
355 let mut g = IndexMap::new();
356 g.insert(
357 "members".to_string(),
358 Value::Array(vec![Value::String("c".to_string())]),
359 );
360 g
361 }),
362 ]),
363 );
364 m
365 });
366
367 let result = eval(&data, ".groups[].members[]").unwrap();
368 assert_eq!(
369 result,
370 Value::Array(vec![
371 Value::String("a".to_string()),
372 Value::String("b".to_string()),
373 Value::String("c".to_string()),
374 ])
375 );
376 }
377
378 #[test]
381 fn test_root_primitive() {
382 let data = Value::String("hello".to_string());
383 let result = eval(&data, ".").unwrap();
384 assert_eq!(result, data);
385 }
386
387 #[test]
388 fn test_root_null() {
389 let data = Value::Null;
390 let result = eval(&data, ".").unwrap();
391 assert_eq!(result, Value::Null);
392 }
393}