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 | Segment::Wildcard => 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 Segment::Slice { start, end, step } => match val {
54 Value::Array(arr) => {
55 let sliced = apply_slice(arr, *start, *end, *step)?;
56 next_results.extend(sliced);
57 }
58 _ => {
59 return Err(DkitError::PathNotFound(
60 "cannot slice non-array value".to_string(),
61 ));
62 }
63 },
64 Segment::RecursiveDescent(name) => {
65 recursive_collect(val, name, &mut next_results);
66 }
67 }
68 }
69
70 results = next_results;
71 }
72
73 let has_iterate = path.segments.iter().any(|s| {
75 matches!(
76 s,
77 Segment::Iterate
78 | Segment::Wildcard
79 | Segment::Slice { .. }
80 | Segment::RecursiveDescent(_)
81 )
82 });
83 if has_iterate {
84 Ok(Value::Array(results))
85 } else {
86 match results.len() {
88 0 => Err(DkitError::PathNotFound("empty result".to_string())),
89 1 => Ok(results.into_iter().next().unwrap()),
90 _ => Ok(Value::Array(results)),
91 }
92 }
93}
94
95fn apply_slice(
97 arr: &[Value],
98 start: Option<i64>,
99 end: Option<i64>,
100 step: Option<i64>,
101) -> Result<Vec<Value>, DkitError> {
102 let len = arr.len() as i64;
103 let step = step.unwrap_or(1);
104
105 if step == 0 {
106 return Err(DkitError::QueryError(
107 "slice step cannot be zero".to_string(),
108 ));
109 }
110
111 let clamp = |idx: i64| -> i64 {
113 if idx < 0 {
114 let resolved = len + idx;
115 if resolved < 0 {
116 0
117 } else {
118 resolved
119 }
120 } else if idx > len {
121 len
122 } else {
123 idx
124 }
125 };
126
127 let (start_idx, end_idx) = if step > 0 {
128 let s = match start {
129 Some(v) => clamp(v),
130 None => 0,
131 };
132 let e = match end {
133 Some(v) => clamp(v),
134 None => len,
135 };
136 (s, e)
137 } else {
138 let s = match start {
139 Some(v) => clamp(v),
140 None => len - 1,
141 };
142 let e = match end {
143 Some(v) => clamp(v),
144 None => -1,
145 };
146 (s, e)
147 };
148
149 let mut result = Vec::new();
150 let mut i = start_idx;
151 if step > 0 {
152 while i < end_idx {
153 if i >= 0 && i < len {
154 result.push(arr[i as usize].clone());
155 }
156 i += step;
157 }
158 } else {
159 while i > end_idx {
160 if i >= 0 && i < len {
161 result.push(arr[i as usize].clone());
162 }
163 i += step;
164 }
165 }
166
167 Ok(result)
168}
169
170fn recursive_collect(value: &Value, key: &str, results: &mut Vec<Value>) {
172 match value {
173 Value::Object(map) => {
174 if let Some(v) = map.get(key) {
175 results.push(v.clone());
176 }
177 for (_, v) in map {
178 recursive_collect(v, key, results);
179 }
180 }
181 Value::Array(arr) => {
182 for item in arr {
183 recursive_collect(item, key, results);
184 }
185 }
186 _ => {}
187 }
188}
189
190fn resolve_index(index: i64, len: usize) -> Result<usize, DkitError> {
192 let resolved = if index < 0 {
193 let positive = (-index) as usize;
194 if positive > len {
195 return Err(DkitError::PathNotFound(format!(
196 "index {} out of bounds (array length: {})",
197 index, len
198 )));
199 }
200 len - positive
201 } else {
202 let idx = index as usize;
203 if idx >= len {
204 return Err(DkitError::PathNotFound(format!(
205 "index {} out of bounds (array length: {})",
206 index, len
207 )));
208 }
209 idx
210 };
211 Ok(resolved)
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::query::parser::parse_query;
218 use indexmap::IndexMap;
219
220 fn eval(value: &Value, query_str: &str) -> Result<Value, DkitError> {
221 let query = parse_query(query_str).unwrap();
222 evaluate_path(value, &query.path)
223 }
224
225 fn sample_data() -> Value {
226 let mut data = IndexMap::new();
237 data.insert("name".to_string(), Value::String("dkit".to_string()));
238 data.insert("version".to_string(), Value::Integer(1));
239
240 let users = vec![
241 {
242 let mut u = IndexMap::new();
243 u.insert("name".to_string(), Value::String("Alice".to_string()));
244 u.insert("age".to_string(), Value::Integer(30));
245 Value::Object(u)
246 },
247 {
248 let mut u = IndexMap::new();
249 u.insert("name".to_string(), Value::String("Bob".to_string()));
250 u.insert("age".to_string(), Value::Integer(25));
251 Value::Object(u)
252 },
253 {
254 let mut u = IndexMap::new();
255 u.insert("name".to_string(), Value::String("Charlie".to_string()));
256 u.insert("age".to_string(), Value::Integer(35));
257 Value::Object(u)
258 },
259 ];
260 data.insert("users".to_string(), Value::Array(users));
261
262 let mut db = IndexMap::new();
263 db.insert("host".to_string(), Value::String("localhost".to_string()));
264 db.insert("port".to_string(), Value::Integer(5432));
265 let mut config = IndexMap::new();
266 config.insert("database".to_string(), Value::Object(db));
267 data.insert("config".to_string(), Value::Object(config));
268
269 Value::Object(data)
270 }
271
272 #[test]
275 fn test_root() {
276 let data = sample_data();
277 let result = eval(&data, ".").unwrap();
278 assert_eq!(result, data);
279 }
280
281 #[test]
284 fn test_field_access() {
285 let data = sample_data();
286 let result = eval(&data, ".name").unwrap();
287 assert_eq!(result, Value::String("dkit".to_string()));
288 }
289
290 #[test]
291 fn test_nested_field() {
292 let data = sample_data();
293 let result = eval(&data, ".config.database.host").unwrap();
294 assert_eq!(result, Value::String("localhost".to_string()));
295 }
296
297 #[test]
298 fn test_nested_field_integer() {
299 let data = sample_data();
300 let result = eval(&data, ".config.database.port").unwrap();
301 assert_eq!(result, Value::Integer(5432));
302 }
303
304 #[test]
305 fn test_field_not_found() {
306 let data = sample_data();
307 let err = eval(&data, ".nonexistent").unwrap_err();
308 assert!(matches!(err, DkitError::PathNotFound(_)));
309 }
310
311 #[test]
312 fn test_field_on_non_object() {
313 let data = sample_data();
314 let err = eval(&data, ".name.sub").unwrap_err();
315 assert!(matches!(err, DkitError::PathNotFound(_)));
316 }
317
318 #[test]
321 fn test_array_index_zero() {
322 let data = sample_data();
323 let result = eval(&data, ".users[0]").unwrap();
324 let obj = result.as_object().unwrap();
325 assert_eq!(obj.get("name"), Some(&Value::String("Alice".to_string())));
326 }
327
328 #[test]
329 fn test_array_index_last() {
330 let data = sample_data();
331 let result = eval(&data, ".users[-1]").unwrap();
332 let obj = result.as_object().unwrap();
333 assert_eq!(obj.get("name"), Some(&Value::String("Charlie".to_string())));
334 }
335
336 #[test]
337 fn test_array_index_with_field() {
338 let data = sample_data();
339 let result = eval(&data, ".users[0].name").unwrap();
340 assert_eq!(result, Value::String("Alice".to_string()));
341 }
342
343 #[test]
344 fn test_array_index_negative_two() {
345 let data = sample_data();
346 let result = eval(&data, ".users[-2].name").unwrap();
347 assert_eq!(result, Value::String("Bob".to_string()));
348 }
349
350 #[test]
351 fn test_array_index_out_of_bounds() {
352 let data = sample_data();
353 let err = eval(&data, ".users[10]").unwrap_err();
354 assert!(matches!(err, DkitError::PathNotFound(_)));
355 }
356
357 #[test]
358 fn test_array_index_negative_out_of_bounds() {
359 let data = sample_data();
360 let err = eval(&data, ".users[-10]").unwrap_err();
361 assert!(matches!(err, DkitError::PathNotFound(_)));
362 }
363
364 #[test]
365 fn test_index_on_non_array() {
366 let data = sample_data();
367 let err = eval(&data, ".name[0]").unwrap_err();
368 assert!(matches!(err, DkitError::PathNotFound(_)));
369 }
370
371 #[test]
374 fn test_iterate() {
375 let data = sample_data();
376 let result = eval(&data, ".users[]").unwrap();
377 let arr = result.as_array().unwrap();
378 assert_eq!(arr.len(), 3);
379 }
380
381 #[test]
382 fn test_iterate_with_field() {
383 let data = sample_data();
384 let result = eval(&data, ".users[].name").unwrap();
385 assert_eq!(
386 result,
387 Value::Array(vec![
388 Value::String("Alice".to_string()),
389 Value::String("Bob".to_string()),
390 Value::String("Charlie".to_string()),
391 ])
392 );
393 }
394
395 #[test]
396 fn test_iterate_with_field_integer() {
397 let data = sample_data();
398 let result = eval(&data, ".users[].age").unwrap();
399 assert_eq!(
400 result,
401 Value::Array(vec![
402 Value::Integer(30),
403 Value::Integer(25),
404 Value::Integer(35),
405 ])
406 );
407 }
408
409 #[test]
410 fn test_iterate_empty_array() {
411 let data = Value::Object({
412 let mut m = IndexMap::new();
413 m.insert("items".to_string(), Value::Array(vec![]));
414 m
415 });
416 let result = eval(&data, ".items[]").unwrap();
417 assert_eq!(result, Value::Array(vec![]));
418 }
419
420 #[test]
421 fn test_iterate_on_non_array() {
422 let data = sample_data();
423 let err = eval(&data, ".name[]").unwrap_err();
424 assert!(matches!(err, DkitError::PathNotFound(_)));
425 }
426
427 #[test]
430 fn test_root_array_index() {
431 let data = Value::Array(vec![
432 Value::Integer(10),
433 Value::Integer(20),
434 Value::Integer(30),
435 ]);
436 let result = eval(&data, ".[0]").unwrap();
437 assert_eq!(result, Value::Integer(10));
438 }
439
440 #[test]
441 fn test_root_array_iterate() {
442 let data = Value::Array(vec![
443 Value::Integer(10),
444 Value::Integer(20),
445 Value::Integer(30),
446 ]);
447 let result = eval(&data, ".[]").unwrap();
448 assert_eq!(result, data);
449 }
450
451 #[test]
454 fn test_nested_iterate() {
455 let data = Value::Object({
456 let mut m = IndexMap::new();
457 m.insert(
458 "groups".to_string(),
459 Value::Array(vec![
460 Value::Object({
461 let mut g = IndexMap::new();
462 g.insert(
463 "members".to_string(),
464 Value::Array(vec![
465 Value::String("a".to_string()),
466 Value::String("b".to_string()),
467 ]),
468 );
469 g
470 }),
471 Value::Object({
472 let mut g = IndexMap::new();
473 g.insert(
474 "members".to_string(),
475 Value::Array(vec![Value::String("c".to_string())]),
476 );
477 g
478 }),
479 ]),
480 );
481 m
482 });
483
484 let result = eval(&data, ".groups[].members[]").unwrap();
485 assert_eq!(
486 result,
487 Value::Array(vec![
488 Value::String("a".to_string()),
489 Value::String("b".to_string()),
490 Value::String("c".to_string()),
491 ])
492 );
493 }
494
495 #[test]
498 fn test_root_primitive() {
499 let data = Value::String("hello".to_string());
500 let result = eval(&data, ".").unwrap();
501 assert_eq!(result, data);
502 }
503
504 #[test]
505 fn test_root_null() {
506 let data = Value::Null;
507 let result = eval(&data, ".").unwrap();
508 assert_eq!(result, Value::Null);
509 }
510
511 #[test]
514 fn test_wildcard_basic() {
515 let data = sample_data();
516 let result = eval(&data, ".users[*]").unwrap();
517 let arr = result.as_array().unwrap();
518 assert_eq!(arr.len(), 3);
519 }
520
521 #[test]
522 fn test_wildcard_with_field() {
523 let data = sample_data();
524 let result = eval(&data, ".users[*].name").unwrap();
525 assert_eq!(
526 result,
527 Value::Array(vec![
528 Value::String("Alice".to_string()),
529 Value::String("Bob".to_string()),
530 Value::String("Charlie".to_string()),
531 ])
532 );
533 }
534
535 #[test]
536 fn test_wildcard_on_non_array() {
537 let data = sample_data();
538 let err = eval(&data, ".name[*]").unwrap_err();
539 assert!(matches!(err, DkitError::PathNotFound(_)));
540 }
541
542 #[test]
545 fn test_slice_basic() {
546 let data = Value::Array(vec![
547 Value::Integer(10),
548 Value::Integer(20),
549 Value::Integer(30),
550 Value::Integer(40),
551 Value::Integer(50),
552 ]);
553 let result = eval(&data, ".[0:3]").unwrap();
554 assert_eq!(
555 result,
556 Value::Array(vec![
557 Value::Integer(10),
558 Value::Integer(20),
559 Value::Integer(30),
560 ])
561 );
562 }
563
564 #[test]
565 fn test_slice_open_end() {
566 let data = Value::Array(vec![
567 Value::Integer(10),
568 Value::Integer(20),
569 Value::Integer(30),
570 Value::Integer(40),
571 Value::Integer(50),
572 ]);
573 let result = eval(&data, ".[2:]").unwrap();
574 assert_eq!(
575 result,
576 Value::Array(vec![
577 Value::Integer(30),
578 Value::Integer(40),
579 Value::Integer(50),
580 ])
581 );
582 }
583
584 #[test]
585 fn test_slice_open_start() {
586 let data = Value::Array(vec![
587 Value::Integer(10),
588 Value::Integer(20),
589 Value::Integer(30),
590 Value::Integer(40),
591 Value::Integer(50),
592 ]);
593 let result = eval(&data, ".[:2]").unwrap();
594 assert_eq!(
595 result,
596 Value::Array(vec![Value::Integer(10), Value::Integer(20),])
597 );
598 }
599
600 #[test]
601 fn test_slice_negative_start() {
602 let data = Value::Array(vec![
603 Value::Integer(10),
604 Value::Integer(20),
605 Value::Integer(30),
606 Value::Integer(40),
607 Value::Integer(50),
608 ]);
609 let result = eval(&data, ".[-2:]").unwrap();
610 assert_eq!(
611 result,
612 Value::Array(vec![Value::Integer(40), Value::Integer(50),])
613 );
614 }
615
616 #[test]
617 fn test_slice_negative_end() {
618 let data = Value::Array(vec![
619 Value::Integer(10),
620 Value::Integer(20),
621 Value::Integer(30),
622 Value::Integer(40),
623 Value::Integer(50),
624 ]);
625 let result = eval(&data, ".[1:-1]").unwrap();
626 assert_eq!(
627 result,
628 Value::Array(vec![
629 Value::Integer(20),
630 Value::Integer(30),
631 Value::Integer(40),
632 ])
633 );
634 }
635
636 #[test]
637 fn test_slice_with_step() {
638 let data = Value::Array(vec![
639 Value::Integer(10),
640 Value::Integer(20),
641 Value::Integer(30),
642 Value::Integer(40),
643 Value::Integer(50),
644 ]);
645 let result = eval(&data, ".[0:5:2]").unwrap();
646 assert_eq!(
647 result,
648 Value::Array(vec![
649 Value::Integer(10),
650 Value::Integer(30),
651 Value::Integer(50),
652 ])
653 );
654 }
655
656 #[test]
657 fn test_slice_reverse() {
658 let data = Value::Array(vec![
659 Value::Integer(10),
660 Value::Integer(20),
661 Value::Integer(30),
662 ]);
663 let result = eval(&data, ".[::-1]").unwrap();
664 assert_eq!(
665 result,
666 Value::Array(vec![
667 Value::Integer(30),
668 Value::Integer(20),
669 Value::Integer(10),
670 ])
671 );
672 }
673
674 #[test]
675 fn test_slice_empty_result() {
676 let data = Value::Array(vec![
677 Value::Integer(10),
678 Value::Integer(20),
679 Value::Integer(30),
680 ]);
681 let result = eval(&data, ".[5:10]").unwrap();
682 assert_eq!(result, Value::Array(vec![]));
683 }
684
685 #[test]
686 fn test_slice_on_nested_field() {
687 let data = sample_data();
688 let result = eval(&data, ".users[0:2]").unwrap();
689 let arr = result.as_array().unwrap();
690 assert_eq!(arr.len(), 2);
691 assert_eq!(
692 arr[0].as_object().unwrap().get("name"),
693 Some(&Value::String("Alice".to_string()))
694 );
695 assert_eq!(
696 arr[1].as_object().unwrap().get("name"),
697 Some(&Value::String("Bob".to_string()))
698 );
699 }
700
701 #[test]
702 fn test_slice_with_field_after() {
703 let data = sample_data();
704 let result = eval(&data, ".users[0:2].name").unwrap();
705 assert_eq!(
706 result,
707 Value::Array(vec![
708 Value::String("Alice".to_string()),
709 Value::String("Bob".to_string()),
710 ])
711 );
712 }
713
714 #[test]
715 fn test_slice_on_non_array() {
716 let data = sample_data();
717 let err = eval(&data, ".name[0:2]").unwrap_err();
718 assert!(matches!(err, DkitError::PathNotFound(_)));
719 }
720
721 #[test]
722 fn test_slice_step_zero_error() {
723 let data = Value::Array(vec![Value::Integer(10)]);
724 let err = eval(&data, ".[::0]").unwrap_err();
725 assert!(matches!(err, DkitError::QueryError(_)));
726 }
727
728 #[test]
729 fn test_slice_full_open() {
730 let data = Value::Array(vec![
731 Value::Integer(10),
732 Value::Integer(20),
733 Value::Integer(30),
734 ]);
735 let result = eval(&data, ".[:]").unwrap();
736 assert_eq!(result, data);
737 }
738
739 #[test]
742 fn test_recursive_descent_simple() {
743 let data = sample_data();
744 let result = eval(&data, "..name").unwrap();
746 assert_eq!(
747 result,
748 Value::Array(vec![
749 Value::String("dkit".to_string()),
750 Value::String("Alice".to_string()),
751 Value::String("Bob".to_string()),
752 Value::String("Charlie".to_string()),
753 ])
754 );
755 }
756
757 #[test]
758 fn test_recursive_descent_nested() {
759 let data = sample_data();
760 let result = eval(&data, "..host").unwrap();
762 assert_eq!(
763 result,
764 Value::Array(vec![Value::String("localhost".to_string())])
765 );
766 }
767
768 #[test]
769 fn test_recursive_descent_no_match() {
770 let data = sample_data();
771 let result = eval(&data, "..nonexistent").unwrap();
772 assert_eq!(result, Value::Array(vec![]));
773 }
774
775 #[test]
776 fn test_recursive_descent_with_prefix_path() {
777 let data = sample_data();
778 let result = eval(&data, ".config..host").unwrap();
780 assert_eq!(
781 result,
782 Value::Array(vec![Value::String("localhost".to_string())])
783 );
784 }
785
786 #[test]
787 fn test_recursive_descent_deeply_nested() {
788 let mut inner = IndexMap::new();
790 inner.insert("id".to_string(), Value::Integer(42));
791
792 let mut mid = IndexMap::new();
793 mid.insert("nested".to_string(), Value::Object(inner));
794 mid.insert("id".to_string(), Value::Integer(1));
795
796 let mut outer = IndexMap::new();
797 outer.insert("data".to_string(), Value::Object(mid));
798
799 let data = Value::Object(outer);
800 let result = eval(&data, "..id").unwrap();
801 assert_eq!(
802 result,
803 Value::Array(vec![Value::Integer(1), Value::Integer(42)])
804 );
805 }
806
807 #[test]
808 fn test_recursive_descent_in_array() {
809 let arr = Value::Array(vec![
811 {
812 let mut m = IndexMap::new();
813 m.insert("x".to_string(), Value::Integer(1));
814 Value::Object(m)
815 },
816 {
817 let mut m = IndexMap::new();
818 m.insert("x".to_string(), Value::Integer(2));
819 Value::Object(m)
820 },
821 ]);
822 let result = eval(&arr, "..x").unwrap();
823 assert_eq!(
824 result,
825 Value::Array(vec![Value::Integer(1), Value::Integer(2)])
826 );
827 }
828}