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