Skip to main content

dkit_core/query/
evaluator.rs

1use crate::error::DkitError;
2use crate::query::parser::{Path, Segment};
3use crate::value::Value;
4
5/// 경로를 사용하여 Value에서 데이터를 추출
6pub 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    // 이터레이션이 있었으면 배열로 반환, 아니면 단일 값
74    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        // 단일 결과
87        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
95/// Python 스타일 배열 슬라이싱 적용
96fn 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    // Python 스타일 인덱스 클램핑
112    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
170/// 재귀적으로 Value 트리를 DFS 순회하며 지정된 키를 가진 값을 수집
171fn 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
190/// 음수 인덱스를 양수로 변환
191fn 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        // {
227        //   "name": "dkit",
228        //   "version": 1,
229        //   "users": [
230        //     {"name": "Alice", "age": 30},
231        //     {"name": "Bob", "age": 25},
232        //     {"name": "Charlie", "age": 35}
233        //   ],
234        //   "config": {"database": {"host": "localhost", "port": 5432}}
235        // }
236        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    // --- 루트 접근 ---
273
274    #[test]
275    fn test_root() {
276        let data = sample_data();
277        let result = eval(&data, ".").unwrap();
278        assert_eq!(result, data);
279    }
280
281    // --- 필드 접근 ---
282
283    #[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    // --- 배열 인덱싱 ---
319
320    #[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    // --- 배열 이터레이션 ---
372
373    #[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    // --- 루트 배열 ---
428
429    #[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    // --- 중첩 이터레이션 ---
452
453    #[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    // --- 프리미티브 값에 대한 루트 접근 ---
496
497    #[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    // --- 배열 와일드카드 ---
512
513    #[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    // --- 배열 슬라이싱 ---
543
544    #[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    // --- 재귀 하강 (recursive descent `..`) ---
740
741    #[test]
742    fn test_recursive_descent_simple() {
743        let data = sample_data();
744        // .name is "dkit", .users[*].name are "Alice", "Bob", "Charlie"
745        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        // .config.database.host = "localhost"
761        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        // .config..host should find "localhost" within config subtree
779        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        // Build a deeply nested structure
789        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        // Array of objects at root
810        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}