Skip to main content

fakecloud_stepfunctions/
jsonpath.rs

1//! JSONPath evaluation for Amazon States Language paths.
2//!
3//! Step Functions evaluates `InputPath`, `OutputPath`, `ItemsPath`, the `.$`
4//! fields of `Parameters` / `ResultSelector` / `ItemSelector`, Choice
5//! `Variable`s and intrinsic arguments with Jayway JsonPath. This module
6//! implements the same syntax and the same result shape:
7//!
8//! - dot and bracket child access: `$.a.b`, `$['a']`, `$["a"]`, `$['a','b']`
9//! - array indexes, including negative ones and unions: `[0]`, `[-1]`, `[0,2]`
10//! - wildcards `.*` / `[*]`, slices `[1:3]`, `[:2]`, `[-2:]`, deep scan `..x`
11//! - filters `[?(@.price < 10 && @.tag == 'x')]`, `[?(@.isbn)]`
12//!
13//! A *definite* path (only single names and single indexes) yields one value
14//! and fails when nothing is found, which Step Functions surfaces as a
15//! `States.Runtime` error. Any other path is *indefinite* and yields a JSON
16//! array of every match (possibly empty).
17
18use serde_json::{Number, Value};
19
20#[derive(Debug, Clone, PartialEq)]
21enum Segment {
22    /// One or more property names (`.a`, `['a']`, `['a','b']`).
23    Names(Vec<String>),
24    /// One or more indexes (`[0]`, `[-1]`, `[0,2]`).
25    Indexes(Vec<i64>),
26    Wildcard,
27    Slice(Option<i64>, Option<i64>),
28    /// `..name`, `..*` or `..[...]`: the inner segment applied at every depth.
29    DeepScan(Box<Segment>),
30    Filter(FilterExpr),
31}
32
33#[derive(Debug, Clone, PartialEq)]
34enum FilterExpr {
35    Or(Box<FilterExpr>, Box<FilterExpr>),
36    And(Box<FilterExpr>, Box<FilterExpr>),
37    Not(Box<FilterExpr>),
38    Exists(Operand),
39    Compare(Operand, CmpOp, Operand),
40}
41
42#[derive(Debug, Clone, Copy, PartialEq)]
43enum CmpOp {
44    Eq,
45    Ne,
46    Lt,
47    Le,
48    Gt,
49    Ge,
50}
51
52#[derive(Debug, Clone, PartialEq)]
53enum Operand {
54    /// `@...` relative to the current item.
55    Current(Vec<Segment>),
56    /// `$...` relative to the document root.
57    Root(Vec<Segment>),
58    Literal(Value),
59}
60
61/// A parsed JSONPath.
62#[derive(Debug, Clone, PartialEq)]
63pub struct JsonPath {
64    segments: Vec<Segment>,
65}
66
67/// One step of a reference path.
68#[derive(Debug, Clone, PartialEq)]
69pub enum Step {
70    Name(String),
71    Index(i64),
72}
73
74/// Why a path could not be evaluated.
75#[derive(Debug, Clone, PartialEq)]
76pub enum PathError {
77    /// Syntactically invalid path.
78    Invalid(String),
79    /// A definite path matched nothing. Carries the normalized path
80    /// (`$['a'][0]`) as Jayway reports it.
81    NoResults(String),
82}
83
84impl JsonPath {
85    /// Parse a path that must start with `$`.
86    pub fn parse(path: &str) -> Result<Self, PathError> {
87        let chars: Vec<char> = path.trim().chars().collect();
88        if chars.first() != Some(&'$') {
89            return Err(PathError::Invalid(format!(
90                "Path must start with '$': {path}"
91            )));
92        }
93        let mut p = Parser { chars, pos: 1 };
94        let segments = p.segments(false)?;
95        if p.pos != p.chars.len() {
96            return Err(PathError::Invalid(format!(
97                "Unexpected character at position {} in path {path}",
98                p.pos
99            )));
100        }
101        Ok(Self { segments })
102    }
103
104    /// True when the path can yield at most one value.
105    pub fn is_definite(&self) -> bool {
106        self.segments.iter().all(|s| match s {
107            Segment::Names(n) => n.len() == 1,
108            Segment::Indexes(i) => i.len() == 1,
109            _ => false,
110        })
111    }
112
113    /// For a definite (reference) path, the chain of property names and
114    /// indexes it walks. `None` for an indefinite path.
115    pub fn reference_steps(&self) -> Option<Vec<Step>> {
116        if !self.is_definite() {
117            return None;
118        }
119        Some(
120            self.segments
121                .iter()
122                .map(|s| match s {
123                    Segment::Names(n) => Step::Name(n[0].clone()),
124                    Segment::Indexes(i) => Step::Index(i[0]),
125                    _ => unreachable!("definite paths only hold names and indexes"),
126                })
127                .collect(),
128        )
129    }
130
131    /// Jayway-style normalized rendering, used in error messages.
132    pub fn normalized(&self) -> String {
133        let mut out = String::from("$");
134        for s in &self.segments {
135            render_segment(s, &mut out);
136        }
137        out
138    }
139
140    /// Evaluate against `root`. Definite paths return the single match or
141    /// [`PathError::NoResults`]; indefinite paths return an array of matches.
142    pub fn evaluate(&self, root: &Value) -> Result<Value, PathError> {
143        let results = select(&self.segments, root, root);
144        if self.is_definite() {
145            results
146                .into_iter()
147                .next()
148                .cloned()
149                .ok_or_else(|| PathError::NoResults(self.normalized()))
150        } else {
151            Ok(Value::Array(results.into_iter().cloned().collect()))
152        }
153    }
154}
155
156/// Parse and evaluate in one step.
157pub fn evaluate(root: &Value, path: &str) -> Result<Value, PathError> {
158    JsonPath::parse(path)?.evaluate(root)
159}
160
161fn render_segment(s: &Segment, out: &mut String) {
162    match s {
163        Segment::Names(names) => {
164            let inner: Vec<String> = names.iter().map(|n| format!("'{n}'")).collect();
165            out.push('[');
166            out.push_str(&inner.join(","));
167            out.push(']');
168        }
169        Segment::Indexes(idx) => {
170            let inner: Vec<String> = idx.iter().map(|i| i.to_string()).collect();
171            out.push('[');
172            out.push_str(&inner.join(","));
173            out.push(']');
174        }
175        Segment::Wildcard => out.push_str("[*]"),
176        Segment::Slice(a, b) => {
177            out.push('[');
178            if let Some(a) = a {
179                out.push_str(&a.to_string());
180            }
181            out.push(':');
182            if let Some(b) = b {
183                out.push_str(&b.to_string());
184            }
185            out.push(']');
186        }
187        Segment::DeepScan(inner) => {
188            out.push_str("..");
189            render_segment(inner, out);
190        }
191        Segment::Filter(_) => out.push_str("[?]"),
192    }
193}
194
195fn select<'a>(segments: &[Segment], node: &'a Value, root: &'a Value) -> Vec<&'a Value> {
196    let mut current = vec![node];
197    for seg in segments {
198        let mut next = Vec::new();
199        for v in current {
200            apply_segment(seg, v, root, &mut next);
201        }
202        current = next;
203    }
204    current
205}
206
207fn apply_segment<'a>(seg: &Segment, v: &'a Value, root: &'a Value, out: &mut Vec<&'a Value>) {
208    match seg {
209        Segment::Names(names) => {
210            if let Value::Object(o) = v {
211                for n in names {
212                    if let Some(child) = o.get(n) {
213                        out.push(child);
214                    }
215                }
216            }
217        }
218        Segment::Indexes(idx) => {
219            if let Value::Array(a) = v {
220                for &i in idx {
221                    if let Some(child) = resolve_index(a, i) {
222                        out.push(child);
223                    }
224                }
225            }
226        }
227        Segment::Wildcard => match v {
228            Value::Array(a) => out.extend(a.iter()),
229            Value::Object(o) => out.extend(o.values()),
230            _ => {}
231        },
232        Segment::Slice(start, end) => {
233            if let Value::Array(a) = v {
234                let len = a.len() as i64;
235                let norm = |x: i64| if x < 0 { (len + x).max(0) } else { x.min(len) };
236                let s = start.map(norm).unwrap_or(0);
237                let e = end.map(norm).unwrap_or(len);
238                if s < e {
239                    out.extend(a[s as usize..e as usize].iter());
240                }
241            }
242        }
243        Segment::DeepScan(inner) => deep_scan(inner, v, root, out),
244        Segment::Filter(expr) => {
245            let items: Vec<&Value> = match v {
246                Value::Array(a) => a.iter().collect(),
247                Value::Object(_) => vec![v],
248                _ => Vec::new(),
249            };
250            for item in items {
251                if eval_filter(expr, item, root) {
252                    out.push(item);
253                }
254            }
255        }
256    }
257}
258
259fn deep_scan<'a>(inner: &Segment, v: &'a Value, root: &'a Value, out: &mut Vec<&'a Value>) {
260    apply_segment(inner, v, root, out);
261    match v {
262        Value::Array(a) => a.iter().for_each(|c| deep_scan(inner, c, root, out)),
263        Value::Object(o) => o.values().for_each(|c| deep_scan(inner, c, root, out)),
264        _ => {}
265    }
266}
267
268fn resolve_index(a: &[Value], i: i64) -> Option<&Value> {
269    let idx = if i < 0 { a.len() as i64 + i } else { i };
270    if idx < 0 {
271        None
272    } else {
273        a.get(idx as usize)
274    }
275}
276
277fn operand_value<'a>(op: &'a Operand, item: &'a Value, root: &'a Value) -> Option<Value> {
278    match op {
279        Operand::Literal(v) => Some(v.clone()),
280        Operand::Current(segs) => select(segs, item, root).first().map(|v| (*v).clone()),
281        Operand::Root(segs) => select(segs, root, root).first().map(|v| (*v).clone()),
282    }
283}
284
285fn eval_filter(expr: &FilterExpr, item: &Value, root: &Value) -> bool {
286    match expr {
287        FilterExpr::Or(a, b) => eval_filter(a, item, root) || eval_filter(b, item, root),
288        FilterExpr::And(a, b) => eval_filter(a, item, root) && eval_filter(b, item, root),
289        FilterExpr::Not(a) => !eval_filter(a, item, root),
290        FilterExpr::Exists(op) => operand_value(op, item, root).is_some(),
291        FilterExpr::Compare(l, op, r) => {
292            let (Some(l), Some(r)) = (operand_value(l, item, root), operand_value(r, item, root))
293            else {
294                return false;
295            };
296            compare(&l, *op, &r)
297        }
298    }
299}
300
301fn compare(l: &Value, op: CmpOp, r: &Value) -> bool {
302    use std::cmp::Ordering;
303    let ord: Option<Ordering> = match (l, r) {
304        (Value::Number(a), Value::Number(b)) => a
305            .as_f64()
306            .zip(b.as_f64())
307            .and_then(|(a, b)| a.partial_cmp(&b)),
308        (Value::String(a), Value::String(b)) => Some(a.cmp(b)),
309        _ => None,
310    };
311    match op {
312        CmpOp::Eq => ord.map(|o| o == Ordering::Equal).unwrap_or(l == r),
313        CmpOp::Ne => ord.map(|o| o != Ordering::Equal).unwrap_or(l != r),
314        CmpOp::Lt => ord == Some(Ordering::Less),
315        CmpOp::Le => matches!(ord, Some(Ordering::Less | Ordering::Equal)),
316        CmpOp::Gt => ord == Some(Ordering::Greater),
317        CmpOp::Ge => matches!(ord, Some(Ordering::Greater | Ordering::Equal)),
318    }
319}
320
321struct Parser {
322    chars: Vec<char>,
323    pos: usize,
324}
325
326impl Parser {
327    fn peek(&self) -> Option<char> {
328        self.chars.get(self.pos).copied()
329    }
330
331    fn peek_at(&self, off: usize) -> Option<char> {
332        self.chars.get(self.pos + off).copied()
333    }
334
335    fn err<T>(&self, msg: &str) -> Result<T, PathError> {
336        Err(PathError::Invalid(format!(
337            "{msg} at position {} in path {}",
338            self.pos,
339            self.chars.iter().collect::<String>()
340        )))
341    }
342
343    fn skip_ws(&mut self) {
344        while self.peek().is_some_and(char::is_whitespace) {
345            self.pos += 1;
346        }
347    }
348
349    /// Parse segments until the end of input, or (inside a filter) until a
350    /// character that cannot continue a path.
351    fn segments(&mut self, in_filter: bool) -> Result<Vec<Segment>, PathError> {
352        let mut out = Vec::new();
353        loop {
354            match self.peek() {
355                Some('.') if self.peek_at(1) == Some('.') => {
356                    self.pos += 2;
357                    let inner = match self.peek() {
358                        Some('[') => self.bracket()?,
359                        Some('*') => {
360                            self.pos += 1;
361                            Segment::Wildcard
362                        }
363                        _ => Segment::Names(vec![self.dot_name()?]),
364                    };
365                    out.push(Segment::DeepScan(Box::new(inner)));
366                }
367                Some('.') => {
368                    self.pos += 1;
369                    if self.peek() == Some('*') {
370                        self.pos += 1;
371                        out.push(Segment::Wildcard);
372                    } else {
373                        out.push(Segment::Names(vec![self.dot_name()?]));
374                    }
375                }
376                Some('[') => {
377                    let seg = self.bracket()?;
378                    out.push(seg);
379                }
380                None => break,
381                Some(_) if in_filter => break,
382                Some(_) => return self.err("Unexpected character"),
383            }
384        }
385        Ok(out)
386    }
387
388    fn dot_name(&mut self) -> Result<String, PathError> {
389        let start = self.pos;
390        while let Some(c) = self.peek() {
391            if c == '.' || c == '[' || c.is_whitespace() || "]()=!<>&|,'\"".contains(c) {
392                break;
393            }
394            self.pos += 1;
395        }
396        if self.pos == start {
397            return self.err("Expected a property name");
398        }
399        Ok(self.chars[start..self.pos].iter().collect())
400    }
401
402    fn quoted(&mut self) -> Result<String, PathError> {
403        let quote = self.peek().unwrap_or('\'');
404        self.pos += 1;
405        let mut s = String::new();
406        loop {
407            match self.peek() {
408                None => return self.err("Unterminated quoted string"),
409                Some('\\') => {
410                    self.pos += 1;
411                    match self.peek() {
412                        Some(c) => {
413                            s.push(c);
414                            self.pos += 1;
415                        }
416                        None => return self.err("Unterminated escape"),
417                    }
418                }
419                Some(c) if c == quote => {
420                    self.pos += 1;
421                    return Ok(s);
422                }
423                Some(c) => {
424                    s.push(c);
425                    self.pos += 1;
426                }
427            }
428        }
429    }
430
431    fn integer(&mut self) -> Option<i64> {
432        let start = self.pos;
433        if self.peek() == Some('-') {
434            self.pos += 1;
435        }
436        while self.peek().is_some_and(|c| c.is_ascii_digit()) {
437            self.pos += 1;
438        }
439        let s: String = self.chars[start..self.pos].iter().collect();
440        match s.parse() {
441            Ok(n) => Some(n),
442            Err(_) => {
443                self.pos = start;
444                None
445            }
446        }
447    }
448
449    fn bracket(&mut self) -> Result<Segment, PathError> {
450        self.pos += 1; // '['
451        self.skip_ws();
452        let seg = match self.peek() {
453            Some('*') => {
454                self.pos += 1;
455                Segment::Wildcard
456            }
457            Some('?') => {
458                self.pos += 1;
459                self.skip_ws();
460                if self.peek() != Some('(') {
461                    return self.err("Expected '(' after '?'");
462                }
463                self.pos += 1;
464                let expr = self.filter_or()?;
465                self.skip_ws();
466                if self.peek() != Some(')') {
467                    return self.err("Expected ')' closing filter");
468                }
469                self.pos += 1;
470                Segment::Filter(expr)
471            }
472            Some('\'') | Some('"') => {
473                let mut names = vec![self.quoted()?];
474                loop {
475                    self.skip_ws();
476                    if self.peek() != Some(',') {
477                        break;
478                    }
479                    self.pos += 1;
480                    self.skip_ws();
481                    if !matches!(self.peek(), Some('\'') | Some('"')) {
482                        return self.err("Expected a quoted property name");
483                    }
484                    names.push(self.quoted()?);
485                }
486                Segment::Names(names)
487            }
488            Some(':') => {
489                self.pos += 1;
490                self.skip_ws();
491                let end = self.integer();
492                Segment::Slice(None, end)
493            }
494            Some(c) if c == '-' || c.is_ascii_digit() => {
495                let first = match self.integer() {
496                    Some(n) => n,
497                    None => return self.err("Invalid array index"),
498                };
499                self.skip_ws();
500                if self.peek() == Some(':') {
501                    self.pos += 1;
502                    self.skip_ws();
503                    let end = self.integer();
504                    Segment::Slice(Some(first), end)
505                } else {
506                    let mut idx = vec![first];
507                    loop {
508                        self.skip_ws();
509                        if self.peek() != Some(',') {
510                            break;
511                        }
512                        self.pos += 1;
513                        self.skip_ws();
514                        match self.integer() {
515                            Some(n) => idx.push(n),
516                            None => return self.err("Invalid array index"),
517                        }
518                    }
519                    Segment::Indexes(idx)
520                }
521            }
522            _ => return self.err("Invalid bracket expression"),
523        };
524        self.skip_ws();
525        if self.peek() != Some(']') {
526            return self.err("Expected ']'");
527        }
528        self.pos += 1;
529        Ok(seg)
530    }
531
532    fn filter_or(&mut self) -> Result<FilterExpr, PathError> {
533        let mut left = self.filter_and()?;
534        loop {
535            self.skip_ws();
536            if self.peek() == Some('|') && self.peek_at(1) == Some('|') {
537                self.pos += 2;
538                let right = self.filter_and()?;
539                left = FilterExpr::Or(Box::new(left), Box::new(right));
540            } else {
541                return Ok(left);
542            }
543        }
544    }
545
546    fn filter_and(&mut self) -> Result<FilterExpr, PathError> {
547        let mut left = self.filter_unary()?;
548        loop {
549            self.skip_ws();
550            if self.peek() == Some('&') && self.peek_at(1) == Some('&') {
551                self.pos += 2;
552                let right = self.filter_unary()?;
553                left = FilterExpr::And(Box::new(left), Box::new(right));
554            } else {
555                return Ok(left);
556            }
557        }
558    }
559
560    fn filter_unary(&mut self) -> Result<FilterExpr, PathError> {
561        self.skip_ws();
562        if self.peek() == Some('!') && self.peek_at(1) != Some('=') {
563            self.pos += 1;
564            return Ok(FilterExpr::Not(Box::new(self.filter_unary()?)));
565        }
566        if self.peek() == Some('(') {
567            self.pos += 1;
568            let inner = self.filter_or()?;
569            self.skip_ws();
570            if self.peek() != Some(')') {
571                return self.err("Expected ')'");
572            }
573            self.pos += 1;
574            return Ok(inner);
575        }
576        let left = self.operand()?;
577        self.skip_ws();
578        let op = match (self.peek(), self.peek_at(1)) {
579            (Some('='), Some('=')) => Some((CmpOp::Eq, 2)),
580            (Some('!'), Some('=')) => Some((CmpOp::Ne, 2)),
581            (Some('<'), Some('=')) => Some((CmpOp::Le, 2)),
582            (Some('>'), Some('=')) => Some((CmpOp::Ge, 2)),
583            (Some('<'), _) => Some((CmpOp::Lt, 1)),
584            (Some('>'), _) => Some((CmpOp::Gt, 1)),
585            _ => None,
586        };
587        match op {
588            Some((op, len)) => {
589                self.pos += len;
590                let right = self.operand()?;
591                Ok(FilterExpr::Compare(left, op, right))
592            }
593            None => match left {
594                Operand::Literal(_) => self.err("A filter needs a path or a comparison"),
595                path => Ok(FilterExpr::Exists(path)),
596            },
597        }
598    }
599
600    fn operand(&mut self) -> Result<Operand, PathError> {
601        self.skip_ws();
602        match self.peek() {
603            Some('@') => {
604                self.pos += 1;
605                Ok(Operand::Current(self.segments(true)?))
606            }
607            Some('$') => {
608                self.pos += 1;
609                Ok(Operand::Root(self.segments(true)?))
610            }
611            Some('\'') | Some('"') => Ok(Operand::Literal(Value::String(self.quoted()?))),
612            Some(_) => {
613                let start = self.pos;
614                while self
615                    .peek()
616                    .is_some_and(|c| c.is_ascii_alphanumeric() || "+-.".contains(c))
617                {
618                    self.pos += 1;
619                }
620                let word: String = self.chars[start..self.pos].iter().collect();
621                let lit = match word.as_str() {
622                    "true" => Value::Bool(true),
623                    "false" => Value::Bool(false),
624                    "null" => Value::Null,
625                    w => {
626                        if let Ok(i) = w.parse::<i64>() {
627                            Value::Number(Number::from(i))
628                        } else if let Some(n) = w.parse::<f64>().ok().and_then(Number::from_f64) {
629                            Value::Number(n)
630                        } else {
631                            return self.err("Invalid filter literal");
632                        }
633                    }
634                };
635                Ok(Operand::Literal(lit))
636            }
637            None => self.err("Unexpected end of filter"),
638        }
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use super::*;
645    use serde_json::json;
646
647    fn doc() -> Value {
648        json!({
649            "store": {
650                "book": [
651                    {"category": "ref", "author": "Nigel", "price": 8.95},
652                    {"category": "fiction", "author": "Evelyn", "price": 12.99, "isbn": "x"},
653                    {"category": "fiction", "author": "Herman", "price": 8.99, "isbn": "y"}
654                ],
655                "bicycle": {"color": "red", "price": 19.95}
656            },
657            "weird key": 1,
658            "n": null
659        })
660    }
661
662    #[test]
663    fn definite_paths() {
664        let d = doc();
665        assert_eq!(evaluate(&d, "$").unwrap(), d);
666        assert_eq!(evaluate(&d, "$.store.bicycle.color").unwrap(), json!("red"));
667        assert_eq!(
668            evaluate(&d, "$['store']['bicycle']['color']").unwrap(),
669            json!("red")
670        );
671        assert_eq!(evaluate(&d, "$[\"weird key\"]").unwrap(), json!(1));
672        assert_eq!(
673            evaluate(&d, "$.store.book[0].author").unwrap(),
674            json!("Nigel")
675        );
676        assert_eq!(
677            evaluate(&d, "$.store.book[-1].author").unwrap(),
678            json!("Herman")
679        );
680        // A present null is a result, not a miss.
681        assert_eq!(evaluate(&d, "$.n").unwrap(), Value::Null);
682    }
683
684    #[test]
685    fn definite_miss_is_no_results() {
686        let d = doc();
687        assert_eq!(
688            evaluate(&d, "$.store.missing").unwrap_err(),
689            PathError::NoResults("$['store']['missing']".into())
690        );
691        assert!(matches!(
692            evaluate(&d, "$.store.book[9]"),
693            Err(PathError::NoResults(_))
694        ));
695        assert!(matches!(
696            evaluate(&d, "$.store.bicycle.color.x"),
697            Err(PathError::NoResults(_))
698        ));
699    }
700
701    #[test]
702    fn indefinite_paths_return_arrays() {
703        let d = doc();
704        assert_eq!(
705            evaluate(&d, "$.store.book[*].author").unwrap(),
706            json!(["Nigel", "Evelyn", "Herman"])
707        );
708        assert_eq!(
709            evaluate(&d, "$.store.book[0,2].price").unwrap(),
710            json!([8.95, 8.99])
711        );
712        assert_eq!(
713            evaluate(&d, "$.store.book[1:].author").unwrap(),
714            json!(["Evelyn", "Herman"])
715        );
716        assert_eq!(
717            evaluate(&d, "$.store.book[:1].author").unwrap(),
718            json!(["Nigel"])
719        );
720        assert_eq!(
721            evaluate(&d, "$.store.book[-2:].author").unwrap(),
722            json!(["Evelyn", "Herman"])
723        );
724        assert_eq!(evaluate(&d, "$..color").unwrap(), json!(["red"]));
725        assert_eq!(
726            evaluate(&d, "$.store..price")
727                .unwrap()
728                .as_array()
729                .unwrap()
730                .len(),
731            4
732        );
733        // No match on an indefinite path is an empty array, not an error.
734        assert_eq!(evaluate(&d, "$.store.book[*].nope").unwrap(), json!([]));
735        assert_eq!(
736            evaluate(&d, "$['store']['book','bicycle']")
737                .unwrap()
738                .as_array()
739                .unwrap()
740                .len(),
741            2
742        );
743    }
744
745    #[test]
746    fn filters() {
747        let d = doc();
748        assert_eq!(
749            evaluate(&d, "$.store.book[?(@.price < 10)].author").unwrap(),
750            json!(["Nigel", "Herman"])
751        );
752        assert_eq!(
753            evaluate(&d, "$.store.book[?(@.isbn)].author").unwrap(),
754            json!(["Evelyn", "Herman"])
755        );
756        assert_eq!(
757            evaluate(
758                &d,
759                "$.store.book[?(@.category == 'fiction' && @.price > 10)].author"
760            )
761            .unwrap(),
762            json!(["Evelyn"])
763        );
764        assert_eq!(
765            evaluate(
766                &d,
767                "$.store.book[?(@.author == \"Nigel\" || @.price >= 12.99)].author"
768            )
769            .unwrap(),
770            json!(["Nigel", "Evelyn"])
771        );
772        assert_eq!(
773            evaluate(&d, "$.store.book[?(@.price > $.store.bicycle.price)]").unwrap(),
774            json!([])
775        );
776        assert_eq!(
777            evaluate(&d, "$.store.book[?(!(@.isbn))].author").unwrap(),
778            json!(["Nigel"])
779        );
780    }
781
782    #[test]
783    fn invalid_paths() {
784        for p in [
785            "", "foo", "$.", "$[", "$.a[", "$.x[é]", "$[]", "$.a]", "$['a",
786        ] {
787            assert!(
788                matches!(JsonPath::parse(p), Err(PathError::Invalid(_))),
789                "{p}"
790            );
791        }
792    }
793
794    #[test]
795    fn definiteness() {
796        assert!(JsonPath::parse("$.a[0]['b']").unwrap().is_definite());
797        assert!(JsonPath::parse("$.a[-1]").unwrap().is_definite());
798        assert!(!JsonPath::parse("$.a[*]").unwrap().is_definite());
799        assert!(!JsonPath::parse("$..a").unwrap().is_definite());
800        assert!(!JsonPath::parse("$.a[0,1]").unwrap().is_definite());
801        assert!(!JsonPath::parse("$.a[0:1]").unwrap().is_definite());
802    }
803}