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 }
65 }
66
67 results = next_results;
68 }
69
70 let has_iterate = path.segments.iter().any(|s| {
72 matches!(
73 s,
74 Segment::Iterate | Segment::Wildcard | Segment::Slice { .. }
75 )
76 });
77 if has_iterate {
78 Ok(Value::Array(results))
79 } else {
80 match results.len() {
82 0 => Err(DkitError::PathNotFound("empty result".to_string())),
83 1 => Ok(results.into_iter().next().unwrap()),
84 _ => Ok(Value::Array(results)),
85 }
86 }
87}
88
89fn apply_slice(
91 arr: &[Value],
92 start: Option<i64>,
93 end: Option<i64>,
94 step: Option<i64>,
95) -> Result<Vec<Value>, DkitError> {
96 let len = arr.len() as i64;
97 let step = step.unwrap_or(1);
98
99 if step == 0 {
100 return Err(DkitError::QueryError(
101 "slice step cannot be zero".to_string(),
102 ));
103 }
104
105 let clamp = |idx: i64| -> i64 {
107 if idx < 0 {
108 let resolved = len + idx;
109 if resolved < 0 {
110 0
111 } else {
112 resolved
113 }
114 } else if idx > len {
115 len
116 } else {
117 idx
118 }
119 };
120
121 let (start_idx, end_idx) = if step > 0 {
122 let s = match start {
123 Some(v) => clamp(v),
124 None => 0,
125 };
126 let e = match end {
127 Some(v) => clamp(v),
128 None => len,
129 };
130 (s, e)
131 } else {
132 let s = match start {
133 Some(v) => clamp(v),
134 None => len - 1,
135 };
136 let e = match end {
137 Some(v) => clamp(v),
138 None => -1,
139 };
140 (s, e)
141 };
142
143 let mut result = Vec::new();
144 let mut i = start_idx;
145 if step > 0 {
146 while i < end_idx {
147 if i >= 0 && i < len {
148 result.push(arr[i as usize].clone());
149 }
150 i += step;
151 }
152 } else {
153 while i > end_idx {
154 if i >= 0 && i < len {
155 result.push(arr[i as usize].clone());
156 }
157 i += step;
158 }
159 }
160
161 Ok(result)
162}
163
164fn resolve_index(index: i64, len: usize) -> Result<usize, DkitError> {
166 let resolved = if index < 0 {
167 let positive = (-index) as usize;
168 if positive > len {
169 return Err(DkitError::PathNotFound(format!(
170 "index {} out of bounds (array length: {})",
171 index, len
172 )));
173 }
174 len - positive
175 } else {
176 let idx = index as usize;
177 if idx >= len {
178 return Err(DkitError::PathNotFound(format!(
179 "index {} out of bounds (array length: {})",
180 index, len
181 )));
182 }
183 idx
184 };
185 Ok(resolved)
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::query::parser::parse_query;
192 use indexmap::IndexMap;
193
194 fn eval(value: &Value, query_str: &str) -> Result<Value, DkitError> {
195 let query = parse_query(query_str).unwrap();
196 evaluate_path(value, &query.path)
197 }
198
199 fn sample_data() -> Value {
200 let mut data = IndexMap::new();
211 data.insert("name".to_string(), Value::String("dkit".to_string()));
212 data.insert("version".to_string(), Value::Integer(1));
213
214 let users = vec![
215 {
216 let mut u = IndexMap::new();
217 u.insert("name".to_string(), Value::String("Alice".to_string()));
218 u.insert("age".to_string(), Value::Integer(30));
219 Value::Object(u)
220 },
221 {
222 let mut u = IndexMap::new();
223 u.insert("name".to_string(), Value::String("Bob".to_string()));
224 u.insert("age".to_string(), Value::Integer(25));
225 Value::Object(u)
226 },
227 {
228 let mut u = IndexMap::new();
229 u.insert("name".to_string(), Value::String("Charlie".to_string()));
230 u.insert("age".to_string(), Value::Integer(35));
231 Value::Object(u)
232 },
233 ];
234 data.insert("users".to_string(), Value::Array(users));
235
236 let mut db = IndexMap::new();
237 db.insert("host".to_string(), Value::String("localhost".to_string()));
238 db.insert("port".to_string(), Value::Integer(5432));
239 let mut config = IndexMap::new();
240 config.insert("database".to_string(), Value::Object(db));
241 data.insert("config".to_string(), Value::Object(config));
242
243 Value::Object(data)
244 }
245
246 #[test]
249 fn test_root() {
250 let data = sample_data();
251 let result = eval(&data, ".").unwrap();
252 assert_eq!(result, data);
253 }
254
255 #[test]
258 fn test_field_access() {
259 let data = sample_data();
260 let result = eval(&data, ".name").unwrap();
261 assert_eq!(result, Value::String("dkit".to_string()));
262 }
263
264 #[test]
265 fn test_nested_field() {
266 let data = sample_data();
267 let result = eval(&data, ".config.database.host").unwrap();
268 assert_eq!(result, Value::String("localhost".to_string()));
269 }
270
271 #[test]
272 fn test_nested_field_integer() {
273 let data = sample_data();
274 let result = eval(&data, ".config.database.port").unwrap();
275 assert_eq!(result, Value::Integer(5432));
276 }
277
278 #[test]
279 fn test_field_not_found() {
280 let data = sample_data();
281 let err = eval(&data, ".nonexistent").unwrap_err();
282 assert!(matches!(err, DkitError::PathNotFound(_)));
283 }
284
285 #[test]
286 fn test_field_on_non_object() {
287 let data = sample_data();
288 let err = eval(&data, ".name.sub").unwrap_err();
289 assert!(matches!(err, DkitError::PathNotFound(_)));
290 }
291
292 #[test]
295 fn test_array_index_zero() {
296 let data = sample_data();
297 let result = eval(&data, ".users[0]").unwrap();
298 let obj = result.as_object().unwrap();
299 assert_eq!(obj.get("name"), Some(&Value::String("Alice".to_string())));
300 }
301
302 #[test]
303 fn test_array_index_last() {
304 let data = sample_data();
305 let result = eval(&data, ".users[-1]").unwrap();
306 let obj = result.as_object().unwrap();
307 assert_eq!(obj.get("name"), Some(&Value::String("Charlie".to_string())));
308 }
309
310 #[test]
311 fn test_array_index_with_field() {
312 let data = sample_data();
313 let result = eval(&data, ".users[0].name").unwrap();
314 assert_eq!(result, Value::String("Alice".to_string()));
315 }
316
317 #[test]
318 fn test_array_index_negative_two() {
319 let data = sample_data();
320 let result = eval(&data, ".users[-2].name").unwrap();
321 assert_eq!(result, Value::String("Bob".to_string()));
322 }
323
324 #[test]
325 fn test_array_index_out_of_bounds() {
326 let data = sample_data();
327 let err = eval(&data, ".users[10]").unwrap_err();
328 assert!(matches!(err, DkitError::PathNotFound(_)));
329 }
330
331 #[test]
332 fn test_array_index_negative_out_of_bounds() {
333 let data = sample_data();
334 let err = eval(&data, ".users[-10]").unwrap_err();
335 assert!(matches!(err, DkitError::PathNotFound(_)));
336 }
337
338 #[test]
339 fn test_index_on_non_array() {
340 let data = sample_data();
341 let err = eval(&data, ".name[0]").unwrap_err();
342 assert!(matches!(err, DkitError::PathNotFound(_)));
343 }
344
345 #[test]
348 fn test_iterate() {
349 let data = sample_data();
350 let result = eval(&data, ".users[]").unwrap();
351 let arr = result.as_array().unwrap();
352 assert_eq!(arr.len(), 3);
353 }
354
355 #[test]
356 fn test_iterate_with_field() {
357 let data = sample_data();
358 let result = eval(&data, ".users[].name").unwrap();
359 assert_eq!(
360 result,
361 Value::Array(vec![
362 Value::String("Alice".to_string()),
363 Value::String("Bob".to_string()),
364 Value::String("Charlie".to_string()),
365 ])
366 );
367 }
368
369 #[test]
370 fn test_iterate_with_field_integer() {
371 let data = sample_data();
372 let result = eval(&data, ".users[].age").unwrap();
373 assert_eq!(
374 result,
375 Value::Array(vec![
376 Value::Integer(30),
377 Value::Integer(25),
378 Value::Integer(35),
379 ])
380 );
381 }
382
383 #[test]
384 fn test_iterate_empty_array() {
385 let data = Value::Object({
386 let mut m = IndexMap::new();
387 m.insert("items".to_string(), Value::Array(vec![]));
388 m
389 });
390 let result = eval(&data, ".items[]").unwrap();
391 assert_eq!(result, Value::Array(vec![]));
392 }
393
394 #[test]
395 fn test_iterate_on_non_array() {
396 let data = sample_data();
397 let err = eval(&data, ".name[]").unwrap_err();
398 assert!(matches!(err, DkitError::PathNotFound(_)));
399 }
400
401 #[test]
404 fn test_root_array_index() {
405 let data = Value::Array(vec![
406 Value::Integer(10),
407 Value::Integer(20),
408 Value::Integer(30),
409 ]);
410 let result = eval(&data, ".[0]").unwrap();
411 assert_eq!(result, Value::Integer(10));
412 }
413
414 #[test]
415 fn test_root_array_iterate() {
416 let data = Value::Array(vec![
417 Value::Integer(10),
418 Value::Integer(20),
419 Value::Integer(30),
420 ]);
421 let result = eval(&data, ".[]").unwrap();
422 assert_eq!(result, data);
423 }
424
425 #[test]
428 fn test_nested_iterate() {
429 let data = Value::Object({
430 let mut m = IndexMap::new();
431 m.insert(
432 "groups".to_string(),
433 Value::Array(vec![
434 Value::Object({
435 let mut g = IndexMap::new();
436 g.insert(
437 "members".to_string(),
438 Value::Array(vec![
439 Value::String("a".to_string()),
440 Value::String("b".to_string()),
441 ]),
442 );
443 g
444 }),
445 Value::Object({
446 let mut g = IndexMap::new();
447 g.insert(
448 "members".to_string(),
449 Value::Array(vec![Value::String("c".to_string())]),
450 );
451 g
452 }),
453 ]),
454 );
455 m
456 });
457
458 let result = eval(&data, ".groups[].members[]").unwrap();
459 assert_eq!(
460 result,
461 Value::Array(vec![
462 Value::String("a".to_string()),
463 Value::String("b".to_string()),
464 Value::String("c".to_string()),
465 ])
466 );
467 }
468
469 #[test]
472 fn test_root_primitive() {
473 let data = Value::String("hello".to_string());
474 let result = eval(&data, ".").unwrap();
475 assert_eq!(result, data);
476 }
477
478 #[test]
479 fn test_root_null() {
480 let data = Value::Null;
481 let result = eval(&data, ".").unwrap();
482 assert_eq!(result, Value::Null);
483 }
484
485 #[test]
488 fn test_wildcard_basic() {
489 let data = sample_data();
490 let result = eval(&data, ".users[*]").unwrap();
491 let arr = result.as_array().unwrap();
492 assert_eq!(arr.len(), 3);
493 }
494
495 #[test]
496 fn test_wildcard_with_field() {
497 let data = sample_data();
498 let result = eval(&data, ".users[*].name").unwrap();
499 assert_eq!(
500 result,
501 Value::Array(vec![
502 Value::String("Alice".to_string()),
503 Value::String("Bob".to_string()),
504 Value::String("Charlie".to_string()),
505 ])
506 );
507 }
508
509 #[test]
510 fn test_wildcard_on_non_array() {
511 let data = sample_data();
512 let err = eval(&data, ".name[*]").unwrap_err();
513 assert!(matches!(err, DkitError::PathNotFound(_)));
514 }
515
516 #[test]
519 fn test_slice_basic() {
520 let data = Value::Array(vec![
521 Value::Integer(10),
522 Value::Integer(20),
523 Value::Integer(30),
524 Value::Integer(40),
525 Value::Integer(50),
526 ]);
527 let result = eval(&data, ".[0:3]").unwrap();
528 assert_eq!(
529 result,
530 Value::Array(vec![
531 Value::Integer(10),
532 Value::Integer(20),
533 Value::Integer(30),
534 ])
535 );
536 }
537
538 #[test]
539 fn test_slice_open_end() {
540 let data = Value::Array(vec![
541 Value::Integer(10),
542 Value::Integer(20),
543 Value::Integer(30),
544 Value::Integer(40),
545 Value::Integer(50),
546 ]);
547 let result = eval(&data, ".[2:]").unwrap();
548 assert_eq!(
549 result,
550 Value::Array(vec![
551 Value::Integer(30),
552 Value::Integer(40),
553 Value::Integer(50),
554 ])
555 );
556 }
557
558 #[test]
559 fn test_slice_open_start() {
560 let data = Value::Array(vec![
561 Value::Integer(10),
562 Value::Integer(20),
563 Value::Integer(30),
564 Value::Integer(40),
565 Value::Integer(50),
566 ]);
567 let result = eval(&data, ".[:2]").unwrap();
568 assert_eq!(
569 result,
570 Value::Array(vec![Value::Integer(10), Value::Integer(20),])
571 );
572 }
573
574 #[test]
575 fn test_slice_negative_start() {
576 let data = Value::Array(vec![
577 Value::Integer(10),
578 Value::Integer(20),
579 Value::Integer(30),
580 Value::Integer(40),
581 Value::Integer(50),
582 ]);
583 let result = eval(&data, ".[-2:]").unwrap();
584 assert_eq!(
585 result,
586 Value::Array(vec![Value::Integer(40), Value::Integer(50),])
587 );
588 }
589
590 #[test]
591 fn test_slice_negative_end() {
592 let data = Value::Array(vec![
593 Value::Integer(10),
594 Value::Integer(20),
595 Value::Integer(30),
596 Value::Integer(40),
597 Value::Integer(50),
598 ]);
599 let result = eval(&data, ".[1:-1]").unwrap();
600 assert_eq!(
601 result,
602 Value::Array(vec![
603 Value::Integer(20),
604 Value::Integer(30),
605 Value::Integer(40),
606 ])
607 );
608 }
609
610 #[test]
611 fn test_slice_with_step() {
612 let data = Value::Array(vec![
613 Value::Integer(10),
614 Value::Integer(20),
615 Value::Integer(30),
616 Value::Integer(40),
617 Value::Integer(50),
618 ]);
619 let result = eval(&data, ".[0:5:2]").unwrap();
620 assert_eq!(
621 result,
622 Value::Array(vec![
623 Value::Integer(10),
624 Value::Integer(30),
625 Value::Integer(50),
626 ])
627 );
628 }
629
630 #[test]
631 fn test_slice_reverse() {
632 let data = Value::Array(vec![
633 Value::Integer(10),
634 Value::Integer(20),
635 Value::Integer(30),
636 ]);
637 let result = eval(&data, ".[::-1]").unwrap();
638 assert_eq!(
639 result,
640 Value::Array(vec![
641 Value::Integer(30),
642 Value::Integer(20),
643 Value::Integer(10),
644 ])
645 );
646 }
647
648 #[test]
649 fn test_slice_empty_result() {
650 let data = Value::Array(vec![
651 Value::Integer(10),
652 Value::Integer(20),
653 Value::Integer(30),
654 ]);
655 let result = eval(&data, ".[5:10]").unwrap();
656 assert_eq!(result, Value::Array(vec![]));
657 }
658
659 #[test]
660 fn test_slice_on_nested_field() {
661 let data = sample_data();
662 let result = eval(&data, ".users[0:2]").unwrap();
663 let arr = result.as_array().unwrap();
664 assert_eq!(arr.len(), 2);
665 assert_eq!(
666 arr[0].as_object().unwrap().get("name"),
667 Some(&Value::String("Alice".to_string()))
668 );
669 assert_eq!(
670 arr[1].as_object().unwrap().get("name"),
671 Some(&Value::String("Bob".to_string()))
672 );
673 }
674
675 #[test]
676 fn test_slice_with_field_after() {
677 let data = sample_data();
678 let result = eval(&data, ".users[0:2].name").unwrap();
679 assert_eq!(
680 result,
681 Value::Array(vec![
682 Value::String("Alice".to_string()),
683 Value::String("Bob".to_string()),
684 ])
685 );
686 }
687
688 #[test]
689 fn test_slice_on_non_array() {
690 let data = sample_data();
691 let err = eval(&data, ".name[0:2]").unwrap_err();
692 assert!(matches!(err, DkitError::PathNotFound(_)));
693 }
694
695 #[test]
696 fn test_slice_step_zero_error() {
697 let data = Value::Array(vec![Value::Integer(10)]);
698 let err = eval(&data, ".[::0]").unwrap_err();
699 assert!(matches!(err, DkitError::QueryError(_)));
700 }
701
702 #[test]
703 fn test_slice_full_open() {
704 let data = Value::Array(vec![
705 Value::Integer(10),
706 Value::Integer(20),
707 Value::Integer(30),
708 ]);
709 let result = eval(&data, ".[:]").unwrap();
710 assert_eq!(result, data);
711 }
712}