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            }
65        }
66
67        results = next_results;
68    }
69
70    // 이터레이션이 있었으면 배열로 반환, 아니면 단일 값
71    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        // 단일 결과
81        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
89/// Python 스타일 배열 슬라이싱 적용
90fn 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    // Python 스타일 인덱스 클램핑
106    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
164/// 음수 인덱스를 양수로 변환
165fn 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        // {
201        //   "name": "dkit",
202        //   "version": 1,
203        //   "users": [
204        //     {"name": "Alice", "age": 30},
205        //     {"name": "Bob", "age": 25},
206        //     {"name": "Charlie", "age": 35}
207        //   ],
208        //   "config": {"database": {"host": "localhost", "port": 5432}}
209        // }
210        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    // --- 루트 접근 ---
247
248    #[test]
249    fn test_root() {
250        let data = sample_data();
251        let result = eval(&data, ".").unwrap();
252        assert_eq!(result, data);
253    }
254
255    // --- 필드 접근 ---
256
257    #[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    // --- 배열 인덱싱 ---
293
294    #[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    // --- 배열 이터레이션 ---
346
347    #[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    // --- 루트 배열 ---
402
403    #[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    // --- 중첩 이터레이션 ---
426
427    #[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    // --- 프리미티브 값에 대한 루트 접근 ---
470
471    #[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    // --- 배열 와일드카드 ---
486
487    #[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    // --- 배열 슬라이싱 ---
517
518    #[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}