jsonpath_plus/ast/
eval.rs

1use super::*;
2use crate::eval::EvalCtx;
3use either::Either;
4use std::borrow::Cow;
5
6use crate::utils::ValueExt;
7use serde_json::Value;
8
9fn flatten_recur<'a>(collect: &mut Vec<&'a Value>, a: &'a Value) {
10    collect.push(a);
11    a.iter().for_each(|a| flatten_recur(collect, a));
12}
13
14impl Path {
15    pub(crate) fn has_parent(&self) -> bool {
16        for op in &self.segments {
17            let result = match op {
18                Segment::Dot(_, RawSelector::Parent(_))
19                | Segment::Recursive(_, Some(RawSelector::Parent(_)))
20                | Segment::Bracket(_, BracketSelector::Parent(_)) => true,
21                Segment::Bracket(_, BracketSelector::Path(p)) => p.has_parent(),
22                Segment::Bracket(_, BracketSelector::Filter(f)) => f.has_parent(),
23                _ => false,
24            };
25
26            if result {
27                return true;
28            }
29        }
30        false
31    }
32
33    pub(crate) fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
34        for op in &self.segments {
35            op.eval(ctx);
36        }
37        if self.tilde.is_some() {
38            unimplemented!(
39                "Tilde at the top level isn't yet supported due to API design questions. Please \
40                raise an issue with your use case"
41            )
42        }
43    }
44}
45
46impl Segment {
47    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
48        match self {
49            Segment::Dot(_, op) => op.eval(ctx),
50            Segment::Bracket(_, op) => op.eval(ctx),
51            Segment::Recursive(_, op) => {
52                ctx.apply_matched(|_, a| {
53                    let mut all = Vec::new();
54                    flatten_recur(&mut all, a);
55                    all
56                });
57                if let Some(inner) = op {
58                    inner.eval(ctx);
59                }
60            }
61        }
62    }
63}
64
65impl RawSelector {
66    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
67        match self {
68            RawSelector::Wildcard(_) => ctx.apply_matched(|_, a| a.iter()),
69            RawSelector::Parent(_) => {
70                ctx.apply_matched(|ctx, a| ctx.parent_of(a));
71            }
72            RawSelector::Name(name) => ctx.apply_matched(|_, a| match a {
73                Value::Object(m) => m.get(name.as_str()),
74                _ => None,
75            }),
76        }
77    }
78}
79
80fn step_handle(val: i64) -> (bool, usize) {
81    if val < 0 {
82        (true, val.abs() as usize)
83    } else {
84        (false, val as usize)
85    }
86}
87
88fn idx_handle(val: i64, slice: &[Value]) -> Option<usize> {
89    if val < 0 {
90        slice.len().checked_sub(val.abs() as usize)
91    } else {
92        Some(val as usize)
93    }
94}
95
96fn range(slice: &[Value], start: usize, end: usize) -> &[Value] {
97    if start > end || start > slice.len() {
98        &[]
99    } else if end >= slice.len() {
100        &slice[start..]
101    } else {
102        &slice[start..end]
103    }
104}
105
106impl StepRange {
107    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
108        let start = self.start.as_ref().map_or(0, |i| i.as_int());
109        let end = self.end.as_ref().map_or(i64::MAX, |i| i.as_int());
110        let step = self.step.as_ref().map_or(1, |i| i.as_int().get());
111
112        let (rev, step) = step_handle(step);
113
114        ctx.apply_matched(|_, a| match a {
115            Value::Array(v) => {
116                let start = idx_handle(start, v).unwrap_or(0);
117                let end = idx_handle(end, v).unwrap_or(0);
118
119                let iter = range(v, start, end).iter();
120
121                if rev {
122                    Either::Left(iter.rev().step_by(step))
123                } else {
124                    Either::Right(iter.step_by(step))
125                }
126            }
127            _ => Either::Right([].iter().step_by(1)),
128        });
129    }
130}
131
132impl Range {
133    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
134        let start = self.start.as_ref().map_or(0, |i| i.as_int());
135        let end = self.end.as_ref().map_or(i64::MAX, |i| i.as_int());
136
137        ctx.apply_matched(|_, a| match a {
138            Value::Array(v) => {
139                let start = idx_handle(start, v).unwrap_or(0);
140                let end = idx_handle(end, v).unwrap_or(0);
141
142                range(v, start, end)
143            }
144            _ => &[],
145        });
146    }
147}
148
149impl UnionComponent {
150    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
151        match self {
152            UnionComponent::StepRange(step_range) => step_range.eval(ctx),
153            UnionComponent::Range(range) => range.eval(ctx),
154            UnionComponent::Parent(_) => {
155                ctx.apply_matched(|ctx, a| ctx.parent_of(a));
156            }
157            UnionComponent::Path(path) => {
158                path.eval_match(ctx);
159            }
160            UnionComponent::Filter(filter) => {
161                filter.eval(ctx);
162            }
163            UnionComponent::Literal(lit) => {
164                lit.eval(ctx);
165            }
166        }
167    }
168}
169
170impl BracketSelector {
171    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
172        match self {
173            BracketSelector::Union(components) => {
174                let mut new_matched = Vec::new();
175                let old_matched = ctx.get_matched().to_owned();
176                for component in components {
177                    ctx.set_matched(old_matched.clone());
178                    component.eval(ctx);
179                    new_matched.extend(ctx.get_matched());
180                }
181                ctx.set_matched(new_matched);
182            }
183            BracketSelector::StepRange(step_range) => step_range.eval(ctx),
184            BracketSelector::Range(range) => range.eval(ctx),
185            BracketSelector::Wildcard(_) => ctx.apply_matched(|_, a| a.iter()),
186            BracketSelector::Parent(_) => {
187                ctx.apply_matched(|ctx, a| ctx.parent_of(a));
188            }
189            BracketSelector::Path(path) => {
190                path.eval_match(ctx);
191            }
192            BracketSelector::Filter(filter) => {
193                filter.eval(ctx);
194            }
195            BracketSelector::Literal(lit) => {
196                lit.eval(ctx);
197            }
198        }
199    }
200}
201
202impl BracketLit {
203    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
204        match self {
205            BracketLit::Int(i) => ctx.apply_matched(|_, a| match a {
206                Value::Array(v) => idx_handle(i.as_int(), v).and_then(|idx| v.get(idx)),
207                _ => None,
208            }),
209            BracketLit::String(s) => ctx.apply_matched(|_, a| match a {
210                Value::Object(m) => m.get(s.as_str()),
211                _ => None,
212            }),
213        }
214    }
215}
216
217impl SubPath {
218    pub(crate) fn has_parent(&self) -> bool {
219        for op in &self.segments {
220            let result = match op {
221                Segment::Dot(_, RawSelector::Parent(_))
222                | Segment::Recursive(_, Some(RawSelector::Parent(_)))
223                | Segment::Bracket(_, BracketSelector::Parent(_)) => true,
224                Segment::Bracket(_, BracketSelector::Path(p)) => p.has_parent(),
225                Segment::Bracket(_, BracketSelector::Filter(f)) => f.has_parent(),
226                _ => false,
227            };
228
229            if result {
230                return true;
231            }
232        }
233        false
234    }
235
236    fn eval_expr<'a>(&self, ctx: &EvalCtx<'a, '_>, a: &'a Value) -> Option<Cow<'a, Value>> {
237        let relative = match self.kind {
238            PathKind::Root(_) => false,
239            PathKind::Relative(_) => true,
240        };
241
242        let new_root = if relative { a } else { ctx.root() };
243
244        let mut new_ctx = EvalCtx::new_parents(new_root, ctx.all_parents());
245        for op in &self.segments {
246            op.eval(&mut new_ctx);
247        }
248        let matched = new_ctx.into_matched();
249
250        if matched.len() == 1 {
251            let matched = if self.tilde.is_some() {
252                Cow::Owned(ctx.idx_of(matched[0])?.into())
253            } else {
254                Cow::Borrowed(matched[0])
255            };
256
257            Some(matched)
258        } else {
259            None
260        }
261    }
262
263    fn eval_match(&self, ctx: &mut EvalCtx<'_, '_>) {
264        let relative = match self.kind {
265            PathKind::Root(_) => false,
266            PathKind::Relative(_) => true,
267        };
268
269        ctx.set_matched(ctx.apply_matched_ref(|ctx, a| {
270            let new_root = if relative { a } else { ctx.root() };
271
272            let mut new_ctx = EvalCtx::new_parents(new_root, ctx.all_parents());
273            for op in &self.segments {
274                op.eval(&mut new_ctx);
275            }
276
277            let id = self.tilde.is_some();
278
279            new_ctx
280                .into_matched()
281                .into_iter()
282                .map(move |a| {
283                    if id {
284                        Cow::Owned(ctx.idx_of(a).unwrap().into())
285                    } else {
286                        Cow::Borrowed(a)
287                    }
288                })
289                .flat_map(move |mat| match a {
290                    Value::Array(v) => {
291                        let idx = match &*mat {
292                            Value::Number(n) => idx_handle(n.as_i64().unwrap(), v),
293                            _ => None,
294                        };
295                        idx.and_then(|i| v.get(i))
296                    }
297                    Value::Object(m) => {
298                        let idx = match &*mat {
299                            Value::String(s) => Some(s.to_string()),
300                            Value::Number(n) => Some(n.to_string()),
301                            _ => None,
302                        };
303
304                        idx.and_then(|i| m.get(&i))
305                    }
306                    _ => None,
307                })
308        }));
309    }
310}
311
312impl Filter {
313    fn has_parent(&self) -> bool {
314        self.inner.has_parent()
315    }
316
317    fn eval(&self, ctx: &mut EvalCtx<'_, '_>) {
318        ctx.set_matched(ctx.apply_matched_ref(|ctx, a| {
319            a.iter().filter(|&a| {
320                self.inner
321                    .eval_expr(ctx, a)
322                    .map_or(false, |c| c.as_bool() == Some(true))
323            })
324        }));
325    }
326}
327
328impl FilterExpr {
329    fn has_parent(&self) -> bool {
330        match self {
331            FilterExpr::Unary(_, inner) => inner.has_parent(),
332            FilterExpr::Binary(left, _, right) => left.has_parent() || right.has_parent(),
333            FilterExpr::Parens(_, inner) => inner.has_parent(),
334            FilterExpr::Path(p) => p.has_parent(),
335            _ => false,
336        }
337    }
338
339    fn eval_expr<'a>(&self, ctx: &EvalCtx<'a, '_>, val: &'a Value) -> Option<Cow<'a, Value>> {
340        match self {
341            FilterExpr::Unary(op, inner) => {
342                let inner = inner.eval_expr(ctx, val)?;
343
344                match op {
345                    UnOp::Neg(_) => match &*inner {
346                        Value::Number(n) => {
347                            let out = n
348                                .as_i64()
349                                .map(|i| Value::from(-i))
350                                .or_else(|| n.as_u64().map(|i| Value::from(-(i as i64))))
351                                .or_else(|| n.as_f64().map(|f| Value::from(-f)));
352                            Some(Cow::Owned(out.unwrap()))
353                        }
354                        _ => None,
355                    },
356                    UnOp::Not(_) => match &*inner {
357                        Value::Bool(b) => Some(Cow::Owned(Value::from(!b))),
358                        _ => None,
359                    },
360                }
361            }
362            FilterExpr::Binary(lhs, op, rhs) => {
363                let lhs = lhs.eval_expr(ctx, val)?;
364                let rhs = rhs.eval_expr(ctx, val)?;
365
366                match op {
367                    BinOp::And(_) => {
368                        let lhs = lhs.as_bool()?;
369                        let rhs = rhs.as_bool()?;
370                        Some(Cow::Owned(Value::Bool(lhs && rhs)))
371                    }
372                    BinOp::Or(_) => {
373                        let lhs = lhs.as_bool()?;
374                        let rhs = rhs.as_bool()?;
375                        Some(Cow::Owned(Value::Bool(lhs || rhs)))
376                    }
377
378                    BinOp::Eq(_) => Some(Cow::Owned(Value::Bool(lhs == rhs))),
379                    BinOp::Le(_) => {
380                        let lhs = lhs.as_f64()?;
381                        let rhs = rhs.as_f64()?;
382
383                        Some(Cow::Owned(Value::Bool(lhs <= rhs)))
384                    }
385                    BinOp::Lt(_) => {
386                        let lhs = lhs.as_f64()?;
387                        let rhs = rhs.as_f64()?;
388
389                        Some(Cow::Owned(Value::Bool(lhs < rhs)))
390                    }
391                    BinOp::Gt(_) => {
392                        let lhs = lhs.as_f64()?;
393                        let rhs = rhs.as_f64()?;
394
395                        Some(Cow::Owned(Value::Bool(lhs > rhs)))
396                    }
397                    BinOp::Ge(_) => {
398                        let lhs = lhs.as_f64()?;
399                        let rhs = rhs.as_f64()?;
400
401                        Some(Cow::Owned(Value::Bool(lhs >= rhs)))
402                    }
403
404                    BinOp::Add(_) => {
405                        if lhs.is_f64() && rhs.is_f64() {
406                            let lhs = lhs.as_f64()?;
407                            let rhs = rhs.as_f64()?;
408
409                            Some(Cow::Owned(Value::from(lhs + rhs)))
410                        } else if lhs.is_string() && rhs.is_string() {
411                            let lhs = lhs.as_str()?;
412                            let rhs = rhs.as_str()?;
413
414                            Some(Cow::Owned(Value::String(format!("{lhs}{rhs}"))))
415                        } else {
416                            None
417                        }
418                    }
419                    BinOp::Sub(_) => {
420                        let lhs = lhs.as_f64()?;
421                        let rhs = rhs.as_f64()?;
422
423                        Some(Cow::Owned(Value::from(lhs - rhs)))
424                    }
425                    BinOp::Mul(_) => {
426                        let lhs = lhs.as_f64()?;
427                        let rhs = rhs.as_f64()?;
428
429                        Some(Cow::Owned(Value::from(lhs * rhs)))
430                    }
431                    BinOp::Div(_) => {
432                        let lhs = lhs.as_f64()?;
433                        let rhs = rhs.as_f64()?;
434
435                        Some(Cow::Owned(Value::from(lhs / rhs)))
436                    }
437                    BinOp::Rem(_) => {
438                        let lhs = lhs.as_i64()?;
439                        let rhs = rhs.as_i64()?;
440
441                        Some(Cow::Owned(Value::from(lhs % rhs)))
442                    }
443                }
444            }
445            FilterExpr::Path(path) => path.eval_expr(ctx, val),
446            FilterExpr::Lit(lit) => Some(Cow::Owned(match lit {
447                ExprLit::Int(i) => Value::from(i.as_int()),
448                ExprLit::String(s) => Value::from(s.as_str()),
449                ExprLit::Bool(b) => Value::from(b.as_bool()),
450                ExprLit::Null(_) => Value::Null,
451            })),
452            FilterExpr::Parens(_, inner) => inner.eval_expr(ctx, val),
453        }
454    }
455}