jsonpath_rust/query/
selector.rs

1use crate::parser::model::Selector;
2use crate::query::queryable::Queryable;
3use crate::query::state::{Data, Pointer, State};
4use crate::query::Query;
5use std::cmp::{max, min};
6
7impl Query for Selector {
8    fn process<'a, T: Queryable>(&self, step: State<'a, T>) -> State<'a, T> {
9        match self {
10            Selector::Name(key) => step.flat_map(|d| process_key(d, key)),
11            Selector::Index(idx) => step.flat_map(|d| process_index(d, idx)),
12            Selector::Wildcard => step.flat_map(process_wildcard),
13            Selector::Slice(start, end, sl_step) => {
14                step.flat_map(|d| process_slice(d, start, end, sl_step))
15            }
16            Selector::Filter(f) => f.process(step),
17        }
18    }
19}
20
21fn process_wildcard<T: Queryable>(
22    Pointer {
23        inner: pointer,
24        path,
25    }: Pointer<T>,
26) -> Data<T> {
27    if let Some(array) = pointer.as_array() {
28        if array.is_empty() {
29            Data::Nothing
30        } else {
31            Data::new_refs(
32                array
33                    .iter()
34                    .enumerate()
35                    .map(|(i, elem)| Pointer::idx(elem, path.clone(), i))
36                    .collect(),
37            )
38        }
39    } else if let Some(object) = pointer.as_object() {
40        if object.is_empty() {
41            Data::Nothing
42        } else {
43            Data::new_refs(
44                object
45                    .into_iter()
46                    .map(|(key, value)| Pointer::key(value, path.clone(), key))
47                    .collect(),
48            )
49        }
50    } else {
51        Data::Nothing
52    }
53}
54
55fn process_slice<'a, T: Queryable>(
56    Pointer { inner, path }: Pointer<'a, T>,
57    start: &Option<i64>,
58    end: &Option<i64>,
59    step: &Option<i64>,
60) -> Data<'a, T> {
61    let extract_elems = |elements: &'a Vec<T>| -> Vec<(&'a T, usize)> {
62        let len = elements.len() as i64;
63        let norm = |i: i64| {
64            if i >= 0 {
65                i
66            } else {
67                len + i
68            }
69        };
70
71        match step.unwrap_or(1) {
72            e if e > 0 => {
73                let n_start = norm(start.unwrap_or(0));
74                let n_end = norm(end.unwrap_or(len));
75                let lower = min(max(n_start, 0), len);
76                let upper = min(max(n_end, 0), len);
77
78                let mut idx = lower;
79                let mut res = vec![];
80                while idx < upper {
81                    let i = idx as usize;
82                    if let Some(elem) = elements.get(i) {
83                        res.push((elem, i));
84                    }
85                    idx += e;
86                }
87                res
88            }
89            e if e < 0 => {
90                let n_start = norm(start.unwrap_or(len - 1));
91                let n_end = norm(end.unwrap_or(-len - 1));
92                let lower = min(max(n_end, -1), len - 1);
93                let upper = min(max(n_start, -1), len - 1);
94                let mut idx = upper;
95                let mut res = vec![];
96                while lower < idx {
97                    let i = idx as usize;
98                    if let Some(elem) = elements.get(i) {
99                        res.push((elem, i));
100                    }
101                    idx += e;
102                }
103                res
104            }
105            _ => vec![],
106        }
107    };
108
109    let elems_to_step = |v: Vec<(&'a T, usize)>| {
110        Data::new_refs(
111            v.into_iter()
112                .map(|(elem, i)| Pointer::idx(elem, path.clone(), i))
113                .collect(),
114        )
115    };
116
117    inner
118        .as_array()
119        .map(extract_elems)
120        .map(elems_to_step)
121        .unwrap_or_default()
122}
123
124/// Processes escape sequences in JSON strings
125/// - Replaces `\\` with `\`
126/// - Replaces `\/` with `/`
127/// - Preserves other valid escapes like `\"` and `\'`
128fn normalize_json_key(input: &str) -> String {
129    let mut result = String::with_capacity(input.len());
130    let mut chars = input.chars().peekable();
131
132    while let Some(c) = chars.next() {
133        if c == '\\' {
134            if let Some(&next) = chars.peek() {
135                match next {
136                    '\\' => {
137                        result.push('\\');
138                        chars.next(); // consume the second backslash
139                    }
140                    '/' => {
141                        result.push('/');
142                        chars.next(); // consume the forward slash
143                    }
144                    '\'' => {
145                        result.push('\\');
146                        result.push('\'');
147                        chars.next(); // consume the quote
148                    }
149                    '"' => {
150                        result.push('\\');
151                        result.push('"');
152                        chars.next(); // consume the quote
153                    }
154                    'b' | 'f' | 'n' | 'r' | 't' | 'u' => {
155                        // Preserve these standard JSON escape sequences
156                        result.push('\\');
157                        result.push(next);
158                        chars.next();
159                    }
160                    _ => {
161                        // Invalid escape - just keep as-is
162                        result.push('\\');
163                    }
164                }
165            } else {
166                // Trailing backslash
167                result.push('\\');
168            }
169        } else {
170            result.push(c);
171        }
172    }
173    result
174}
175
176pub fn process_key<'a, T: Queryable>(
177    Pointer { inner, path }: Pointer<'a, T>,
178    key: &str,
179) -> Data<'a, T> {
180    inner
181        .get(normalize_json_key(key).as_str())
182        .map(|v| Data::new_ref(Pointer::key(v, path, key)))
183        .unwrap_or_default()
184}
185
186pub fn process_index<'a, T: Queryable>(
187    Pointer { inner, path }: Pointer<'a, T>,
188    idx: &i64,
189) -> Data<'a, T> {
190    inner
191        .as_array()
192        .map(|array| {
193            if *idx >= 0 {
194                if *idx >= array.len() as i64 {
195                    Data::Nothing
196                } else {
197                    let i = *idx as usize;
198                    Data::new_ref(Pointer::idx(&array[i], path, i))
199                }
200            } else {
201                let abs_idx = idx.abs() as usize;
202                if abs_idx > array.len() {
203                    Data::Nothing
204                } else {
205                    let i = array.len() - abs_idx;
206                    Data::new_ref(Pointer::idx(&array[i], path, i))
207                }
208            }
209        })
210        .unwrap_or_default()
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use crate::parser::model::Segment;
217    use crate::query::{js_path, Queried};
218    use serde_json::json;
219    use std::vec;
220    #[test]
221    fn test_process_empty_key() {
222        let value = json!({" ": "value"});
223        let segment = Segment::Selector(Selector::Name(" ".to_string()));
224
225        let step = segment.process(State::root(&value));
226
227        assert_eq!(
228            step.ok_ref(),
229            Some(vec![Pointer::new(&json!("value"), "$[' ']".to_string())])
230        );
231    }
232    #[test]
233    fn test_process_key() {
234        let value = json!({"key": "value"});
235        let segment = Segment::Selector(Selector::Name("key".to_string()));
236
237        let step = segment.process(State::root(&value));
238
239        assert_eq!(
240            step.ok_ref(),
241            Some(vec![Pointer::new(&json!("value"), "$['key']".to_string())])
242        );
243    }
244
245    #[test]
246    fn test_process_key_failed() {
247        let value = json!({"key": "value"});
248        let segment = Segment::Selector(Selector::Name("key2".to_string()));
249        let step = segment.process(State::root(&value));
250
251        assert_eq!(step, State::nothing(&value));
252    }
253
254    #[test]
255    fn test_process_index() {
256        let value = json!([1, 2, 3]);
257        let segment = Segment::Selector(Selector::Index(1));
258        let step = segment.process(State::root(&value));
259
260        assert_eq!(
261            step.ok_ref(),
262            Some(vec![Pointer::new(&json!(2), "$[1]".to_string())])
263        );
264    }
265
266    #[test]
267    fn test_process_index_failed() {
268        let value = json!([1, 2, 3]);
269        let segment = Segment::Selector(Selector::Index(3));
270        let step = segment.process(State::root(&value));
271
272        assert_eq!(step, State::nothing(&value));
273    }
274
275    #[test]
276    fn test_process_slice1() {
277        let value = json!([1, 2, 3, 4, 5]);
278        let segment = Segment::Selector(Selector::Slice(Some(1), Some(4), Some(1)));
279        let step = segment.process(State::root(&value));
280
281        assert_eq!(
282            step.ok_ref(),
283            Some(vec![
284                Pointer::new(&json!(2), "$[1]".to_string()),
285                Pointer::new(&json!(3), "$[2]".to_string()),
286                Pointer::new(&json!(4), "$[3]".to_string())
287            ])
288        );
289    }
290
291    #[test]
292    fn test_process_slice2() {
293        let value = json!([1, 2, 3, 4, 5]);
294        let segment = Segment::Selector(Selector::Slice(Some(2), Some(0), Some(-1)));
295        let step = segment.process(State::root(&value));
296
297        assert_eq!(
298            step.ok_ref(),
299            Some(vec![
300                Pointer::new(&json!(3), "$[2]".to_string()),
301                Pointer::new(&json!(2), "$[1]".to_string()),
302            ])
303        );
304    }
305
306    #[test]
307    fn test_process_slice3() {
308        let value = json!([1, 2, 3, 4, 5]);
309        let segment = Segment::Selector(Selector::Slice(Some(0), Some(5), Some(2)));
310        let step = segment.process(State::root(&value));
311
312        assert_eq!(
313            step.ok_ref(),
314            Some(vec![
315                Pointer::new(&json!(1), "$[0]".to_string()),
316                Pointer::new(&json!(3), "$[2]".to_string()),
317                Pointer::new(&json!(5), "$[4]".to_string())
318            ])
319        );
320    }
321
322    #[test]
323    fn test_process_slice_failed() {
324        let value = json!([1, 2, 3, 4, 5]);
325        let segment = Segment::Selector(Selector::Slice(Some(0), Some(5), Some(0)));
326        let step = segment.process(State::root(&value));
327
328        assert_eq!(step.ok_ref(), Some(vec![]));
329    }
330
331    #[test]
332    fn test_process_wildcard() {
333        let value = json!({"key": "value", "key2": "value2"});
334        let segment = Segment::Selector(Selector::Wildcard);
335        let step = segment.process(State::root(&value));
336
337        assert_eq!(
338            step.ok_ref(),
339            Some(vec![
340                Pointer::new(&json!("value"), "$['key']".to_string()),
341                Pointer::new(&json!("value2"), "$['key2']".to_string())
342            ])
343        );
344    }
345
346    #[test]
347    fn test_process_wildcard_array() {
348        let value = json!([1, 2, 3]);
349        let segment = Segment::Selector(Selector::Wildcard);
350        let step = segment.process(State::root(&value));
351
352        assert_eq!(
353            step.ok_ref(),
354            Some(vec![
355                Pointer::new(&json!(1), "$[0]".to_string()),
356                Pointer::new(&json!(2), "$[1]".to_string()),
357                Pointer::new(&json!(3), "$[2]".to_string())
358            ])
359        );
360    }
361
362    #[test]
363    fn test_process_wildcard_failed() {
364        let value = json!(1);
365        let segment = Segment::Selector(Selector::Wildcard);
366        let step = segment.process(State::root(&value));
367
368        assert_eq!(step, State::nothing(&value));
369    }
370
371    #[test]
372    fn multi_selector() -> Queried<()> {
373        let json = json!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
374
375        let vec = js_path("$['a',1]", &json)?;
376
377        assert_eq!(vec, vec![(&json!(1), "$[1]".to_string()).into(),]);
378
379        Ok(())
380    }
381    #[test]
382    fn multi_selector_space() -> Queried<()> {
383        let json = json!({
384          "a": "ab",
385          "b": "bc"
386        });
387
388        let vec = js_path("$['a',\r'b']", &json)?;
389
390        assert_eq!(
391            vec,
392            vec![
393                (&json!("ab"), "$['a']".to_string()).into(),
394                (&json!("bc"), "$['b']".to_string()).into(),
395            ]
396        );
397
398        Ok(())
399    }
400}