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 => 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    // 이터레이션이 있었으면 배열로 반환, 아니면 단일 값
60    let has_iterate = path.segments.iter().any(|s| matches!(s, Segment::Iterate));
61    if has_iterate {
62        Ok(Value::Array(results))
63    } else {
64        // 단일 결과
65        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
73/// 음수 인덱스를 양수로 변환
74fn 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        // {
110        //   "name": "dkit",
111        //   "version": 1,
112        //   "users": [
113        //     {"name": "Alice", "age": 30},
114        //     {"name": "Bob", "age": 25},
115        //     {"name": "Charlie", "age": 35}
116        //   ],
117        //   "config": {"database": {"host": "localhost", "port": 5432}}
118        // }
119        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    // --- 루트 접근 ---
156
157    #[test]
158    fn test_root() {
159        let data = sample_data();
160        let result = eval(&data, ".").unwrap();
161        assert_eq!(result, data);
162    }
163
164    // --- 필드 접근 ---
165
166    #[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    // --- 배열 인덱싱 ---
202
203    #[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    // --- 배열 이터레이션 ---
255
256    #[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    // --- 루트 배열 ---
311
312    #[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    // --- 중첩 이터레이션 ---
335
336    #[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    // --- 프리미티브 값에 대한 루트 접근 ---
379
380    #[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}