Skip to main content

rama_json/path/
mod.rs

1//! JSONPath parsing and typed construction.
2//!
3//! Rama targets RFC 9535 JSONPath. This module implements the selectors that
4//! can be matched efficiently from the concrete path of a value observed by a
5//! forward streaming parser.
6//!
7//! # RFC 9535 support matrix
8//!
9//! | Feature | Status | Notes |
10//! | --- | --- | --- |
11//! | Root selector `$` | supported | Always the implicit path root. |
12//! | Dot member `.name` | supported | RFC identifier shorthand. |
13//! | Bracket member `['name']` / `["name"]` | supported | JSON string escapes, including surrogate pairs. |
14//! | Wildcard `*` | supported | Child and descendant forms. |
15//! | Array index `[0]` | supported | Non-negative indexes. |
16//! | Array slice `[start:end:step]` | supported | Non-negative bounds and non-negative step. A zero step matches no elements. |
17//! | Selector lists / unions `[0,'name',*]` | supported | Child and descendant forms. |
18//! | Descendant segment `..` | supported | Member, index, wildcard, slice, and union selectors. |
19//! | Negative indexes / slices | unsupported | RFC semantics require array length, which a pure forward matcher does not know. |
20//! | Filter selectors `[?(...)]` | unsupported | Requires an expression evaluator and possibly buffered subtrees. |
21//!
22//! # Example
23//!
24//! ```
25//! use rama_json::path::JsonPath;
26//!
27//! let path = JsonPath::builder()
28//!     .member("store")
29//!     .member("book")
30//!     .wildcard()
31//!     .member("author")
32//!     .build();
33//!
34//! assert_eq!(path.to_string(), "$.store.book[*].author");
35//! ```
36
37use std::fmt;
38use std::str::FromStr;
39
40use crate::{JsonError, JsonErrorKind};
41
42const MAX_ARRAY_INDEX: u64 = 9_007_199_254_740_991;
43
44/// A compiled JSONPath expression.
45#[derive(Debug, Clone, PartialEq, Eq, Hash)]
46pub struct JsonPath {
47    segments: Box<[Segment]>,
48}
49
50impl JsonPath {
51    /// Returns a typed JSONPath builder.
52    #[must_use]
53    pub fn builder() -> JsonPathBuilder {
54        JsonPathBuilder::new()
55    }
56
57    /// Builds a path from already typed segments.
58    #[must_use]
59    pub fn from_segments(segments: impl Into<Vec<Segment>>) -> Self {
60        Self {
61            segments: segments.into().into_iter().map(normalize_segment).collect(),
62        }
63    }
64
65    /// Path segments after the root `$`.
66    #[must_use]
67    pub fn segments(&self) -> &[Segment] {
68        &self.segments
69    }
70
71    /// Whether this path only contains exact member and index selectors.
72    #[must_use]
73    pub fn is_singular(&self) -> bool {
74        self.segments
75            .iter()
76            .all(|s| matches!(s, Segment::Member(_) | Segment::Index(_)))
77    }
78
79    /// Returns whether this JSONPath matches an already tracked value path.
80    #[must_use]
81    pub fn matches_path(&self, path: &[PathElement]) -> bool {
82        self.match_count(path) > 0
83    }
84
85    /// Returns how many ways this JSONPath matches an already tracked value
86    /// path.
87    ///
88    /// RFC 9535 selector lists preserve duplicate selections, so overlapping
89    /// union members can match the same concrete path more than once.
90    #[must_use]
91    pub fn match_count(&self, path: &[PathElement]) -> usize {
92        count_matches_from(&self.segments, 0, path, 0)
93    }
94}
95
96impl FromStr for JsonPath {
97    type Err = JsonError;
98
99    fn from_str(s: &str) -> Result<Self, Self::Err> {
100        Parser::new(s).parse()
101    }
102}
103
104impl fmt::Display for JsonPath {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        f.write_str("$")?;
107        for segment in &self.segments {
108            write!(f, "{segment}")?;
109        }
110        Ok(())
111    }
112}
113
114/// One JSONPath selector segment after the root.
115#[derive(Debug, Clone, PartialEq, Eq, Hash)]
116#[non_exhaustive]
117pub enum Segment {
118    /// `.name` / `['name']`.
119    Member(Box<str>),
120    /// `[index]`.
121    Index(usize),
122    /// `[start:end:step]`.
123    Slice(Slice),
124    /// `.*` / `[*]`.
125    Wildcard,
126    /// `[selector, ...]`.
127    Union(Box<[Self]>),
128    /// `..name`.
129    DescendantMember(Box<str>),
130    /// `..[index]`.
131    DescendantIndex(usize),
132    /// `..[start:end:step]`.
133    DescendantSlice(Slice),
134    /// `..*`.
135    DescendantWildcard,
136    /// `..[selector, ...]`.
137    DescendantUnion(Box<[Self]>),
138}
139
140/// RFC 9535 array slice selector with streaming-compatible semantics.
141///
142/// Negative bounds and negative steps are intentionally not represented here:
143/// those require knowing the array length before matching can be correct. A
144/// zero step is represented and simply matches no array elements.
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
146pub struct Slice {
147    start: Option<usize>,
148    end: Option<usize>,
149    step: usize,
150}
151
152impl Slice {
153    /// Creates a slice selector.
154    #[must_use]
155    pub const fn new(start: Option<usize>, end: Option<usize>, step: usize) -> Self {
156        Self { start, end, step }
157    }
158
159    /// Inclusive start bound, or `None` for the default start.
160    #[must_use]
161    pub const fn start(self) -> Option<usize> {
162        self.start
163    }
164
165    /// Exclusive end bound, or `None` for an open-ended slice.
166    #[must_use]
167    pub const fn end(self) -> Option<usize> {
168        self.end
169    }
170
171    /// Step size. A zero step matches no elements.
172    #[must_use]
173    pub const fn step(self) -> usize {
174        self.step
175    }
176
177    /// Returns whether this slice includes `index`.
178    #[must_use]
179    pub fn contains(self, index: usize) -> bool {
180        if self.step == 0 {
181            return false;
182        }
183        let start = self.start.unwrap_or(0);
184        if index < start {
185            return false;
186        }
187        if self.end.is_some_and(|end| index >= end) {
188            return false;
189        }
190        (index - start).is_multiple_of(self.step)
191    }
192}
193
194/// One concrete location segment for a value encountered in a JSON document.
195#[derive(Debug, Clone, PartialEq, Eq, Hash)]
196pub enum PathElement {
197    /// Object member.
198    Member(Box<str>),
199    /// Array element.
200    Index(usize),
201}
202
203impl fmt::Display for PathElement {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        match self {
206            Self::Member(name) if is_dot_member_name(name) => write!(f, ".{name}"),
207            Self::Member(name) => write_quoted_member(f, name),
208            Self::Index(index) => write!(f, "[{index}]"),
209        }
210    }
211}
212
213fn count_matches_from(
214    selector: &[Segment],
215    selector_index: usize,
216    path: &[PathElement],
217    path_index: usize,
218) -> usize {
219    if selector_index == selector.len() {
220        return usize::from(path_index == path.len());
221    }
222
223    match &selector[selector_index] {
224        Segment::Member(expected) => {
225            matches!(path.get(path_index), Some(PathElement::Member(actual)) if actual == expected)
226                .then(|| count_matches_from(selector, selector_index + 1, path, path_index + 1))
227                .unwrap_or(0)
228        }
229        Segment::Index(expected) => {
230            matches!(path.get(path_index), Some(PathElement::Index(actual)) if actual == expected)
231                .then(|| count_matches_from(selector, selector_index + 1, path, path_index + 1))
232                .unwrap_or(0)
233        }
234        Segment::Slice(slice) => {
235            matches!(path.get(path_index), Some(PathElement::Index(actual)) if slice.contains(*actual))
236                .then(|| count_matches_from(selector, selector_index + 1, path, path_index + 1))
237                .unwrap_or(0)
238        }
239        Segment::Wildcard => {
240            if path_index < path.len() {
241                count_matches_from(selector, selector_index + 1, path, path_index + 1)
242            } else {
243                0
244            }
245        }
246        Segment::Union(segments) => {
247            let Some(element) = path.get(path_index) else {
248                return 0;
249            };
250            let child_matches: usize = segments
251                .iter()
252                .map(|segment| child_match_count(segment, element))
253                .sum();
254            child_matches * count_matches_from(selector, selector_index + 1, path, path_index + 1)
255        }
256        Segment::DescendantMember(expected) => {
257            let mut count = 0;
258            for index in path_index..path.len() {
259                if matches!(
260                    path.get(index),
261                    Some(PathElement::Member(actual)) if actual == expected
262                ) {
263                    count += count_matches_from(selector, selector_index + 1, path, index + 1);
264                }
265            }
266            count
267        }
268        Segment::DescendantIndex(expected) => {
269            let mut count = 0;
270            for index in path_index..path.len() {
271                if matches!(path.get(index), Some(PathElement::Index(actual)) if actual == expected)
272                {
273                    count += count_matches_from(selector, selector_index + 1, path, index + 1);
274                }
275            }
276            count
277        }
278        Segment::DescendantSlice(slice) => {
279            let mut count = 0;
280            for index in path_index..path.len() {
281                if matches!(path.get(index), Some(PathElement::Index(actual)) if slice.contains(*actual))
282                {
283                    count += count_matches_from(selector, selector_index + 1, path, index + 1);
284                }
285            }
286            count
287        }
288        Segment::DescendantWildcard => {
289            let mut count = 0;
290            for index in path_index..path.len() {
291                count += count_matches_from(selector, selector_index + 1, path, index + 1);
292            }
293            count
294        }
295        Segment::DescendantUnion(segments) => {
296            let mut count = 0;
297            for index in path_index..path.len() {
298                let Some(element) = path.get(index) else {
299                    continue;
300                };
301                let child_matches: usize = segments
302                    .iter()
303                    .map(|segment| child_match_count(segment, element))
304                    .sum();
305                if child_matches > 0 {
306                    count += child_matches
307                        * count_matches_from(selector, selector_index + 1, path, index + 1);
308                }
309            }
310            count
311        }
312    }
313}
314
315fn child_match_count(selector: &Segment, element: &PathElement) -> usize {
316    match (selector, element) {
317        (Segment::Member(expected), PathElement::Member(actual)) => usize::from(actual == expected),
318        (Segment::Index(expected), PathElement::Index(actual)) => usize::from(actual == expected),
319        (Segment::Slice(slice), PathElement::Index(actual)) => usize::from(slice.contains(*actual)),
320        (Segment::Wildcard, _) => 1,
321        (Segment::Union(segments), _) => segments
322            .iter()
323            .map(|segment| child_match_count(segment, element))
324            .sum(),
325        _ => 0,
326    }
327}
328
329impl fmt::Display for Segment {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        match self {
332            Self::Member(name) if is_dot_member_name(name) => write!(f, ".{name}"),
333            Self::Member(name) => write_quoted_member(f, name),
334            Self::Index(index) => write!(f, "[{index}]"),
335            Self::Slice(slice) => write_slice(f, *slice, false),
336            Self::Wildcard => f.write_str("[*]"),
337            Self::Union(segments) => write_selector_list(f, segments, false),
338            Self::DescendantMember(name) if is_dot_member_name(name) => write!(f, "..{name}"),
339            Self::DescendantMember(name) => {
340                f.write_str("..")?;
341                write_quoted_member(f, name)
342            }
343            Self::DescendantIndex(index) => write!(f, "..[{index}]"),
344            Self::DescendantSlice(slice) => {
345                f.write_str("..")?;
346                write_slice(f, *slice, false)
347            }
348            Self::DescendantWildcard => f.write_str("..*"),
349            Self::DescendantUnion(segments) => write_selector_list(f, segments, true),
350        }
351    }
352}
353
354/// Typed builder for [`JsonPath`].
355#[derive(Debug, Clone, Default)]
356pub struct JsonPathBuilder {
357    segments: Vec<Segment>,
358}
359
360impl JsonPathBuilder {
361    /// Creates a new builder rooted at `$`.
362    #[must_use]
363    pub fn new() -> Self {
364        Self::default()
365    }
366
367    /// Adds a child member selector.
368    #[must_use]
369    pub fn member(mut self, name: impl Into<Box<str>>) -> Self {
370        self.segments.push(Segment::Member(name.into()));
371        self
372    }
373
374    /// Adds a child array index selector.
375    #[must_use]
376    pub fn index(mut self, index: usize) -> Self {
377        self.segments.push(Segment::Index(index));
378        self
379    }
380
381    /// Adds a child array slice selector.
382    #[must_use]
383    pub fn slice(mut self, start: Option<usize>, end: Option<usize>, step: usize) -> Self {
384        self.segments
385            .push(Segment::Slice(Slice::new(start, end, step)));
386        self
387    }
388
389    /// Adds a child wildcard selector.
390    #[must_use]
391    pub fn wildcard(mut self) -> Self {
392        self.segments.push(Segment::Wildcard);
393        self
394    }
395
396    /// Adds a child selector list.
397    #[must_use]
398    pub fn union(mut self, segments: impl Into<Vec<Segment>>) -> Self {
399        self.segments
400            .push(Segment::Union(segments.into().into_boxed_slice()));
401        self
402    }
403
404    /// Adds a descendant member selector.
405    #[must_use]
406    pub fn descendant_member(mut self, name: impl Into<Box<str>>) -> Self {
407        self.segments.push(Segment::DescendantMember(name.into()));
408        self
409    }
410
411    /// Adds a descendant array index selector.
412    #[must_use]
413    pub fn descendant_index(mut self, index: usize) -> Self {
414        self.segments.push(Segment::DescendantIndex(index));
415        self
416    }
417
418    /// Adds a descendant array slice selector.
419    #[must_use]
420    pub fn descendant_slice(
421        mut self,
422        start: Option<usize>,
423        end: Option<usize>,
424        step: usize,
425    ) -> Self {
426        self.segments
427            .push(Segment::DescendantSlice(Slice::new(start, end, step)));
428        self
429    }
430
431    /// Adds a descendant wildcard selector.
432    #[must_use]
433    pub fn descendant_wildcard(mut self) -> Self {
434        self.segments.push(Segment::DescendantWildcard);
435        self
436    }
437
438    /// Adds a descendant selector list.
439    #[must_use]
440    pub fn descendant_union(mut self, segments: impl Into<Vec<Segment>>) -> Self {
441        self.segments
442            .push(Segment::DescendantUnion(segments.into().into_boxed_slice()));
443        self
444    }
445
446    /// Finishes the path.
447    #[must_use]
448    pub fn build(self) -> JsonPath {
449        JsonPath::from_segments(self.segments)
450    }
451}
452
453fn normalize_segment(segment: Segment) -> Segment {
454    match segment {
455        Segment::Union(segments) => {
456            let mut normalized = Vec::new();
457            for segment in segments.into_vec().into_iter().map(normalize_segment) {
458                match segment {
459                    Segment::Union(nested) => normalized.extend(nested.into_vec()),
460                    segment => normalized.push(segment),
461                }
462            }
463            if normalized.len() == 1 {
464                normalized.remove(0)
465            } else {
466                Segment::Union(normalized.into_boxed_slice())
467            }
468        }
469        Segment::DescendantUnion(segments) => {
470            let mut normalized = Vec::new();
471            for segment in segments.into_vec().into_iter().map(normalize_segment) {
472                match segment {
473                    Segment::Union(nested) | Segment::DescendantUnion(nested) => {
474                        normalized.extend(nested.into_vec());
475                    }
476                    segment => normalized.push(segment),
477                }
478            }
479            if normalized.len() == 1 {
480                match normalized.remove(0) {
481                    Segment::Member(name) => Segment::DescendantMember(name),
482                    Segment::Index(index) => Segment::DescendantIndex(index),
483                    Segment::Slice(slice) => Segment::DescendantSlice(slice),
484                    Segment::Wildcard => Segment::DescendantWildcard,
485                    Segment::Union(segments) => Segment::DescendantUnion(segments),
486                    segment @ (Segment::DescendantMember(_)
487                    | Segment::DescendantIndex(_)
488                    | Segment::DescendantSlice(_)
489                    | Segment::DescendantWildcard
490                    | Segment::DescendantUnion(_)) => segment,
491                }
492            } else {
493                Segment::DescendantUnion(normalized.into_boxed_slice())
494            }
495        }
496        segment => segment,
497    }
498}
499
500struct Parser<'a> {
501    input: &'a str,
502    pos: usize,
503}
504
505impl<'a> Parser<'a> {
506    fn new(input: &'a str) -> Self {
507        Self { input, pos: 0 }
508    }
509
510    fn parse(mut self) -> Result<JsonPath, JsonError> {
511        if self.input.is_empty() {
512            return Err(JsonError::new(JsonErrorKind::EmptyPath));
513        }
514        if !self.eat('$') {
515            return Err(JsonError::new(JsonErrorKind::MissingRoot));
516        }
517
518        let mut segments = Vec::new();
519        loop {
520            let whitespace_start = self.pos;
521            self.skip_ws();
522            if self.pos != whitespace_start && self.peek().is_none() {
523                return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
524                    "trailing whitespace",
525                )));
526            }
527            match self.peek() {
528                Some('.') => {
529                    self.bump();
530                    if self.eat('.') {
531                        segments.push(self.parse_descendant()?);
532                    } else {
533                        segments.push(self.parse_dot_child()?);
534                    }
535                }
536                Some('[') => segments.push(self.parse_bracket_child()?),
537                Some(_) => {
538                    return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
539                        "expected selector",
540                    )));
541                }
542                None => break,
543            }
544        }
545
546        Ok(JsonPath {
547            segments: segments.into_boxed_slice(),
548        })
549    }
550
551    fn parse_dot_child(&mut self) -> Result<Segment, JsonError> {
552        if self.eat('*') {
553            return Ok(Segment::Wildcard);
554        }
555        Ok(Segment::Member(self.parse_name()?))
556    }
557
558    fn parse_descendant(&mut self) -> Result<Segment, JsonError> {
559        if self.eat('*') {
560            return Ok(Segment::DescendantWildcard);
561        }
562        if self.peek() == Some('[') {
563            return descendant_from_child(self.parse_bracket_child()?);
564        }
565        Ok(Segment::DescendantMember(self.parse_name()?))
566    }
567
568    fn parse_bracket_child(&mut self) -> Result<Segment, JsonError> {
569        self.expect('[')?;
570        self.skip_ws();
571        let mut selectors = Vec::new();
572        selectors.push(self.parse_bracket_selector()?);
573        self.skip_ws();
574        while self.eat(',') {
575            self.skip_ws();
576            selectors.push(self.parse_bracket_selector()?);
577            self.skip_ws();
578        }
579        self.expect(']')?;
580
581        if selectors.len() == 1 {
582            Ok(selectors.remove(0))
583        } else {
584            Ok(Segment::Union(selectors.into_boxed_slice()))
585        }
586    }
587
588    fn parse_bracket_selector(&mut self) -> Result<Segment, JsonError> {
589        match self.peek() {
590            Some('*') => {
591                self.bump();
592                Ok(Segment::Wildcard)
593            }
594            Some('\'' | '"') => {
595                let name = self.parse_quoted()?;
596                Ok(Segment::Member(name))
597            }
598            Some('?') => Err(JsonError::new(JsonErrorKind::UnsupportedJsonPath(
599                "filter selectors",
600            ))),
601            Some(':') => self.parse_slice(None),
602            Some('-') => Err(JsonError::new(JsonErrorKind::UnsupportedJsonPath(
603                "negative array indices and slices",
604            ))),
605            Some(c) if c.is_ascii_digit() => {
606                let first = self.parse_index()?;
607                self.skip_ws();
608                if self.peek() == Some(':') {
609                    self.parse_slice(Some(first))
610                } else {
611                    Ok(Segment::Index(first))
612                }
613            }
614            _ => Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
615                "invalid bracket selector",
616            ))),
617        }
618    }
619
620    fn parse_slice(&mut self, start: Option<usize>) -> Result<Segment, JsonError> {
621        self.skip_ws();
622        self.expect(':')?;
623        self.skip_ws();
624        let end = self.parse_optional_slice_bound()?;
625        self.skip_ws();
626        let step = if self.eat(':') {
627            self.skip_ws();
628            self.parse_optional_slice_step()?
629        } else {
630            1
631        };
632        Ok(Segment::Slice(Slice::new(start, end, step)))
633    }
634
635    fn parse_optional_slice_bound(&mut self) -> Result<Option<usize>, JsonError> {
636        match self.peek() {
637            Some('-') => Err(JsonError::new(JsonErrorKind::UnsupportedJsonPath(
638                "negative array indices and slices",
639            ))),
640            Some(c) if c.is_ascii_digit() => self.parse_index().map(Some),
641            _ => Ok(None),
642        }
643    }
644
645    fn parse_optional_slice_step(&mut self) -> Result<usize, JsonError> {
646        match self.peek() {
647            Some('-') => Err(JsonError::new(JsonErrorKind::UnsupportedJsonPath(
648                "negative array slices",
649            ))),
650            Some(c) if c.is_ascii_digit() => self.parse_index(),
651            _ => Ok(1),
652        }
653    }
654
655    fn parse_name(&mut self) -> Result<Box<str>, JsonError> {
656        let start = self.pos;
657        match self.peek() {
658            Some(c) if is_name_start(c) => self.bump(),
659            _ => {
660                return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
661                    "expected member name",
662                )));
663            }
664        };
665        while self.peek().is_some_and(is_name_continue) {
666            self.bump();
667        }
668        Ok(self.input[start..self.pos].into())
669    }
670
671    fn parse_index(&mut self) -> Result<usize, JsonError> {
672        let start = self.pos;
673        while self.peek().is_some_and(|c| c.is_ascii_digit()) {
674            self.bump();
675        }
676        let raw = &self.input[start..self.pos];
677        if raw.len() > 1 && raw.starts_with('0') {
678            return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
679                "array index cannot contain leading zeros",
680            )));
681        }
682        let index = raw.parse::<u64>().map_err(|_err| {
683            JsonError::new(JsonErrorKind::InvalidJsonPath("array index overflow"))
684        })?;
685        if index > MAX_ARRAY_INDEX {
686            return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
687                "array index exceeds exact integer range",
688            )));
689        }
690        usize::try_from(index)
691            .map_err(|_err| JsonError::new(JsonErrorKind::InvalidJsonPath("array index overflow")))
692    }
693
694    fn parse_quoted(&mut self) -> Result<Box<str>, JsonError> {
695        let quote = self
696            .bump()
697            .ok_or_else(|| JsonError::new(JsonErrorKind::UnexpectedEnd))?;
698        let mut out = String::new();
699        loop {
700            match self.bump() {
701                Some(c) if c == quote => return Ok(out.into_boxed_str()),
702                Some('\\') => {
703                    let escaped = self
704                        .bump()
705                        .ok_or_else(|| JsonError::new(JsonErrorKind::UnexpectedEnd))?;
706                    match escaped {
707                        '\'' if quote == '\'' => out.push(escaped),
708                        '"' if quote == '"' => out.push(escaped),
709                        '\\' | '/' => out.push(escaped),
710                        'b' => out.push('\u{0008}'),
711                        'f' => out.push('\u{000c}'),
712                        'n' => out.push('\n'),
713                        'r' => out.push('\r'),
714                        't' => out.push('\t'),
715                        'u' => out.push(self.parse_unicode_escape()?),
716                        _ => {
717                            return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
718                                "invalid string escape",
719                            )));
720                        }
721                    }
722                }
723                Some('\u{0000}'..='\u{001f}') => {
724                    return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
725                        "control character in string",
726                    )));
727                }
728                Some(c) => out.push(c),
729                None => return Err(JsonError::new(JsonErrorKind::UnexpectedEnd)),
730            }
731        }
732    }
733
734    fn parse_unicode_escape(&mut self) -> Result<char, JsonError> {
735        let code = self.parse_hex_quad()?;
736        if (0xd800..=0xdbff).contains(&code) {
737            if !self.eat('\\') || !self.eat('u') {
738                return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
739                    "invalid unicode surrogate pair",
740                )));
741            }
742            let low = self.parse_hex_quad()?;
743            if !(0xdc00..=0xdfff).contains(&low) {
744                return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
745                    "invalid unicode surrogate pair",
746                )));
747            }
748            let scalar = 0x10000 + ((code - 0xd800) << 10) + (low - 0xdc00);
749            return char::from_u32(scalar).ok_or_else(|| {
750                JsonError::new(JsonErrorKind::InvalidJsonPath("invalid unicode scalar"))
751            });
752        }
753        if (0xdc00..=0xdfff).contains(&code) {
754            return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
755                "invalid unicode surrogate pair",
756            )));
757        }
758        char::from_u32(code)
759            .ok_or_else(|| JsonError::new(JsonErrorKind::InvalidJsonPath("invalid unicode scalar")))
760    }
761
762    fn parse_hex_quad(&mut self) -> Result<u32, JsonError> {
763        let start = self.pos;
764        for _ in 0..4 {
765            match self.peek() {
766                Some(c) if c.is_ascii_hexdigit() => self.bump(),
767                _ => {
768                    return Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
769                        "invalid unicode escape",
770                    )));
771                }
772            };
773        }
774        u32::from_str_radix(&self.input[start..self.pos], 16).map_err(|_err| {
775            JsonError::new(JsonErrorKind::InvalidJsonPath("invalid unicode escape"))
776        })
777    }
778
779    fn expect(&mut self, expected: char) -> Result<(), JsonError> {
780        if self.eat(expected) {
781            Ok(())
782        } else {
783            Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
784                "unexpected character",
785            )))
786        }
787    }
788
789    fn rest(&self) -> &'a str {
790        self.input.get(self.pos..).unwrap_or("")
791    }
792
793    fn peek(&self) -> Option<char> {
794        self.rest().chars().next()
795    }
796
797    fn bump(&mut self) -> Option<char> {
798        let c = self.peek()?;
799        self.pos += c.len_utf8();
800        Some(c)
801    }
802
803    fn eat(&mut self, expected: char) -> bool {
804        if self.peek() == Some(expected) {
805            self.pos += expected.len_utf8();
806            true
807        } else {
808            false
809        }
810    }
811
812    fn skip_ws(&mut self) {
813        while self
814            .peek()
815            .is_some_and(|c| matches!(c, ' ' | '\n' | '\r' | '\t'))
816        {
817            self.bump();
818        }
819    }
820}
821
822fn is_name_start(c: char) -> bool {
823    c == '_' || c.is_ascii_alphabetic() || !c.is_ascii()
824}
825
826fn is_name_continue(c: char) -> bool {
827    is_name_start(c) || c.is_ascii_digit()
828}
829
830fn is_dot_member_name(name: &str) -> bool {
831    let mut chars = name.chars();
832    chars.next().is_some_and(is_name_start) && chars.all(is_name_continue)
833}
834
835fn descendant_from_child(segment: Segment) -> Result<Segment, JsonError> {
836    match segment {
837        Segment::Member(name) => Ok(Segment::DescendantMember(name)),
838        Segment::Index(index) => Ok(Segment::DescendantIndex(index)),
839        Segment::Slice(slice) => Ok(Segment::DescendantSlice(slice)),
840        Segment::Wildcard => Ok(Segment::DescendantWildcard),
841        Segment::Union(segments) => Ok(Segment::DescendantUnion(segments)),
842        Segment::DescendantMember(_)
843        | Segment::DescendantIndex(_)
844        | Segment::DescendantSlice(_)
845        | Segment::DescendantWildcard
846        | Segment::DescendantUnion(_) => Err(JsonError::new(JsonErrorKind::InvalidJsonPath(
847            "nested descendant selector",
848        ))),
849    }
850}
851
852fn write_quoted_member(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
853    f.write_str("[\"")?;
854    write_json_string_content(f, name)?;
855    f.write_str("\"]")
856}
857
858fn write_selector_list(
859    f: &mut fmt::Formatter<'_>,
860    segments: &[Segment],
861    descendant: bool,
862) -> fmt::Result {
863    if descendant {
864        f.write_str("..")?;
865    }
866    f.write_str("[")?;
867    for (index, segment) in segments.iter().enumerate() {
868        if index > 0 {
869            f.write_str(",")?;
870        }
871        write_bracket_selector(f, segment)?;
872    }
873    f.write_str("]")
874}
875
876fn write_bracket_selector(f: &mut fmt::Formatter<'_>, segment: &Segment) -> fmt::Result {
877    match segment {
878        Segment::Member(name) => {
879            f.write_str("\"")?;
880            write_json_string_content(f, name)?;
881            f.write_str("\"")
882        }
883        Segment::Index(index) => write!(f, "{index}"),
884        Segment::Slice(slice) => write_slice(f, *slice, true),
885        Segment::Wildcard => f.write_str("*"),
886        Segment::Union(segments) => {
887            for (index, segment) in segments.iter().enumerate() {
888                if index > 0 {
889                    f.write_str(",")?;
890                }
891                write_bracket_selector(f, segment)?;
892            }
893            Ok(())
894        }
895        Segment::DescendantMember(_)
896        | Segment::DescendantIndex(_)
897        | Segment::DescendantSlice(_)
898        | Segment::DescendantWildcard
899        | Segment::DescendantUnion(_) => f.write_str("<invalid-descendant-selector>"),
900    }
901}
902
903fn write_slice(f: &mut fmt::Formatter<'_>, slice: Slice, inside_brackets: bool) -> fmt::Result {
904    if !inside_brackets {
905        f.write_str("[")?;
906    }
907    if let Some(start) = slice.start() {
908        write!(f, "{start}")?;
909    }
910    f.write_str(":")?;
911    if let Some(end) = slice.end() {
912        write!(f, "{end}")?;
913    }
914    if slice.step() != 1 {
915        write!(f, ":{}", slice.step())?;
916    }
917    if !inside_brackets {
918        f.write_str("]")?;
919    }
920    Ok(())
921}
922
923fn write_json_string_content(f: &mut fmt::Formatter<'_>, name: &str) -> fmt::Result {
924    for c in name.chars() {
925        match c {
926            '"' => f.write_str("\\\"")?,
927            '\\' => f.write_str("\\\\")?,
928            '\u{0008}' => f.write_str("\\b")?,
929            '\u{000c}' => f.write_str("\\f")?,
930            '\n' => f.write_str("\\n")?,
931            '\r' => f.write_str("\\r")?,
932            '\t' => f.write_str("\\t")?,
933            c if c.is_control() => write!(f, "\\u{:04x}", c as u32)?,
934            c => write!(f, "{c}")?,
935        }
936    }
937    Ok(())
938}
939
940#[cfg(test)]
941mod tests {
942    use super::*;
943
944    #[test]
945    fn parses_supported_selectors() {
946        let cases = [
947            (
948                "$.store.book[*].author",
949                vec![
950                    Segment::Member("store".into()),
951                    Segment::Member("book".into()),
952                    Segment::Wildcard,
953                    Segment::Member("author".into()),
954                ],
955                false,
956            ),
957            (
958                "$['weird.key'][12]",
959                vec![Segment::Member("weird.key".into()), Segment::Index(12)],
960                true,
961            ),
962            (
963                "$..author",
964                vec![Segment::DescendantMember("author".into())],
965                false,
966            ),
967            (
968                "$[0,\"name\",*]",
969                vec![Segment::Union(
970                    vec![
971                        Segment::Index(0),
972                        Segment::Member("name".into()),
973                        Segment::Wildcard,
974                    ]
975                    .into_boxed_slice(),
976                )],
977                false,
978            ),
979            (
980                "$[1:5:2]",
981                vec![Segment::Slice(Slice::new(Some(1), Some(5), 2))],
982                false,
983            ),
984            (
985                "$..[0,\"name\",*]",
986                vec![Segment::DescendantUnion(
987                    vec![
988                        Segment::Index(0),
989                        Segment::Member("name".into()),
990                        Segment::Wildcard,
991                    ]
992                    .into_boxed_slice(),
993                )],
994                false,
995            ),
996            (
997                "$..[1:4]",
998                vec![Segment::DescendantSlice(Slice::new(Some(1), Some(4), 1))],
999                false,
1000            ),
1001        ];
1002
1003        for (input, expected, singular) in cases {
1004            let path: JsonPath = input.parse().unwrap();
1005            assert_eq!(path.segments(), expected.as_slice(), "{input}");
1006            assert_eq!(path.is_singular(), singular, "{input}");
1007        }
1008    }
1009
1010    #[test]
1011    fn builder_matches_parser() {
1012        let built = JsonPath::builder()
1013            .member("store")
1014            .member("book")
1015            .wildcard()
1016            .member("author")
1017            .union([
1018                Segment::Member("name".into()),
1019                Segment::Index(0),
1020                Segment::Slice(Slice::new(None, Some(4), 2)),
1021            ])
1022            .descendant_index(1)
1023            .build();
1024        let parsed: JsonPath = "$.store.book[*].author[\"name\",0,:4:2]..[1]"
1025            .parse()
1026            .unwrap();
1027        assert_eq!(built, parsed);
1028    }
1029
1030    #[test]
1031    fn display_roundtrips_basic_paths() {
1032        for input in [
1033            "$",
1034            "$.a[0].b",
1035            "$['weird.key'][*]",
1036            "$..author",
1037            "$[0,'name',*]",
1038            "$[1:5:2]",
1039            "$[:]",
1040            "$[::3]",
1041            "$..[0,'name',*]",
1042            "$..[1:4]",
1043            "$[\"\\uD834\\uDD1E\"]",
1044        ] {
1045            let path: JsonPath = input.parse().unwrap();
1046            let reparsed: JsonPath = path.to_string().parse().unwrap();
1047            assert_eq!(path, reparsed);
1048        }
1049    }
1050
1051    #[test]
1052    fn display_formats_paths_canonically() {
1053        let cases = [
1054            (
1055                JsonPath::builder().member("alpha").build().to_string(),
1056                "$.alpha",
1057            ),
1058            (
1059                JsonPath::builder().member("weird.key").build().to_string(),
1060                "$[\"weird.key\"]",
1061            ),
1062            (
1063                JsonPath::builder()
1064                    .union([
1065                        Segment::Index(0),
1066                        Segment::Member("name".into()),
1067                        Segment::Wildcard,
1068                    ])
1069                    .build()
1070                    .to_string(),
1071                "$[0,\"name\",*]",
1072            ),
1073            (
1074                JsonPath::builder()
1075                    .descendant_member("alpha")
1076                    .build()
1077                    .to_string(),
1078                "$..alpha",
1079            ),
1080            (
1081                JsonPath::builder()
1082                    .descendant_member("weird.key")
1083                    .build()
1084                    .to_string(),
1085                "$..[\"weird.key\"]",
1086            ),
1087            (
1088                JsonPath::builder()
1089                    .descendant_union([
1090                        Segment::Member("a".into()),
1091                        Segment::Index(1),
1092                        Segment::Wildcard,
1093                    ])
1094                    .build()
1095                    .to_string(),
1096                "$..[\"a\",1,*]",
1097            ),
1098            (
1099                JsonPath::builder().member("\u{1}").build().to_string(),
1100                "$[\"\\u0001\"]",
1101            ),
1102        ];
1103
1104        for (actual, expected) in cases {
1105            assert_eq!(actual, expected);
1106        }
1107
1108        let nested_union = JsonPath::from_segments([Segment::Union(
1109            vec![
1110                Segment::Union(vec![Segment::Index(1), Segment::Index(2)].into_boxed_slice()),
1111                Segment::Index(3),
1112            ]
1113            .into_boxed_slice(),
1114        )]);
1115        assert_eq!(nested_union.to_string(), "$[1,2,3]");
1116        assert_eq!(nested_union, "$[1,2,3]".parse().unwrap());
1117
1118        let nested_descendant_union = JsonPath::from_segments([Segment::DescendantUnion(
1119            vec![
1120                Segment::Union(vec![Segment::Index(1), Segment::Index(2)].into_boxed_slice()),
1121                Segment::Index(3),
1122            ]
1123            .into_boxed_slice(),
1124        )]);
1125        assert_eq!(nested_descendant_union.to_string(), "$..[1,2,3]");
1126        assert_eq!(nested_descendant_union, "$..[1,2,3]".parse().unwrap());
1127
1128        assert_eq!(PathElement::Member("alpha".into()).to_string(), ".alpha");
1129        assert_eq!(
1130            PathElement::Member("weird.key".into()).to_string(),
1131            "[\"weird.key\"]"
1132        );
1133        assert_eq!(PathElement::Index(7).to_string(), "[7]");
1134    }
1135
1136    #[test]
1137    fn typed_builder_covers_all_segment_methods() {
1138        let path = JsonPath::builder()
1139            .index(2)
1140            .slice(Some(1), Some(3), 2)
1141            .descendant_member("x")
1142            .descendant_slice(None, Some(2), 1)
1143            .descendant_wildcard()
1144            .descendant_union([Segment::Member("a".into()), Segment::Index(1)])
1145            .build();
1146
1147        assert_eq!(path.to_string(), "$[2][1:3:2]..x..[:2]..*..[\"a\",1]");
1148    }
1149
1150    #[test]
1151    fn rejects_unsupported_rfc_features_explicitly() {
1152        let cases = [
1153            (
1154                "$[?(@.x)]",
1155                JsonErrorKind::UnsupportedJsonPath("filter selectors"),
1156            ),
1157            (
1158                "$[-1]",
1159                JsonErrorKind::UnsupportedJsonPath("negative array indices and slices"),
1160            ),
1161            (
1162                "$[:-1]",
1163                JsonErrorKind::UnsupportedJsonPath("negative array indices and slices"),
1164            ),
1165            (
1166                "$[1:-1]",
1167                JsonErrorKind::UnsupportedJsonPath("negative array indices and slices"),
1168            ),
1169            (
1170                "$[1:2:-1]",
1171                JsonErrorKind::UnsupportedJsonPath("negative array slices"),
1172            ),
1173        ];
1174
1175        for (input, expected) in cases {
1176            let err = input.parse::<JsonPath>().unwrap_err();
1177            assert_eq!(err.kind(), &expected, "{input}");
1178        }
1179    }
1180
1181    #[test]
1182    fn matches_concrete_paths() {
1183        let path: JsonPath = "$.store.book[*].author".parse().unwrap();
1184        let concrete = [
1185            PathElement::Member("store".into()),
1186            PathElement::Member("book".into()),
1187            PathElement::Index(3),
1188            PathElement::Member("author".into()),
1189        ];
1190        assert!(path.matches_path(&concrete));
1191        assert!(
1192            "$..author"
1193                .parse::<JsonPath>()
1194                .unwrap()
1195                .matches_path(&concrete)
1196        );
1197        assert!(
1198            "$.store.book[1:5:2].author"
1199                .parse::<JsonPath>()
1200                .unwrap()
1201                .matches_path(&concrete)
1202        );
1203        assert!(
1204            "$.store.book[0,3,5].author"
1205                .parse::<JsonPath>()
1206                .unwrap()
1207                .matches_path(&concrete)
1208        );
1209        assert!(
1210            "$.store.book[3].author"
1211                .parse::<JsonPath>()
1212                .unwrap()
1213                .matches_path(&concrete)
1214        );
1215        assert!(
1216            "$..[3].author"
1217                .parse::<JsonPath>()
1218                .unwrap()
1219                .matches_path(&concrete)
1220        );
1221        assert!(
1222            "$..[1:5:2].author"
1223                .parse::<JsonPath>()
1224                .unwrap()
1225                .matches_path(&concrete)
1226        );
1227        assert!(
1228            "$..[\"book\",3].author"
1229                .parse::<JsonPath>()
1230                .unwrap()
1231                .matches_path(&concrete)
1232        );
1233        assert!(
1234            "$.store..*.author"
1235                .parse::<JsonPath>()
1236                .unwrap()
1237                .matches_path(&concrete)
1238        );
1239        assert!(
1240            !"$.store.author"
1241                .parse::<JsonPath>()
1242                .unwrap()
1243                .matches_path(&concrete)
1244        );
1245        assert!(
1246            !"$.store.book[0:3].author"
1247                .parse::<JsonPath>()
1248                .unwrap()
1249                .matches_path(&concrete)
1250        );
1251    }
1252
1253    #[test]
1254    fn slice_matching_is_start_inclusive_end_exclusive() {
1255        let cases = [
1256            ("$[1:4]", 0, false),
1257            ("$[1:4]", 1, true),
1258            ("$[1:4]", 3, true),
1259            ("$[1:4]", 4, false),
1260            ("$[1:6:2]", 1, true),
1261            ("$[1:6:2]", 2, false),
1262            ("$[1:6:2]", 5, true),
1263            ("$[:3]", 0, true),
1264            ("$[:3]", 3, false),
1265            ("$[2:]", 100, true),
1266            ("$[2::3]", 5, true),
1267            ("$[2::3]", 4, false),
1268            ("$[::0]", 0, false),
1269            ("$[::0]", 1, false),
1270        ];
1271
1272        for (selector, index, expected) in cases {
1273            let path: JsonPath = selector.parse().unwrap();
1274            assert_eq!(
1275                path.matches_path(&[PathElement::Index(index)]),
1276                expected,
1277                "{selector} vs [{index}]"
1278            );
1279        }
1280
1281        assert!(
1282            !"$[*]".parse::<JsonPath>().unwrap().matches_path(&[]),
1283            "wildcard must not match a missing path element"
1284        );
1285    }
1286
1287    #[test]
1288    fn selectors_do_not_match_when_tail_or_current_element_differs() {
1289        let cases = [
1290            ("$.a.b", vec![PathElement::Member("a".into())]),
1291            (
1292                "$.a.b",
1293                vec![
1294                    PathElement::Member("a".into()),
1295                    PathElement::Member("x".into()),
1296                ],
1297            ),
1298            (
1299                "$.a[*].b",
1300                vec![PathElement::Member("a".into()), PathElement::Index(0)],
1301            ),
1302            (
1303                "$.a[*].b",
1304                vec![
1305                    PathElement::Member("a".into()),
1306                    PathElement::Member("x".into()),
1307                    PathElement::Member("c".into()),
1308                ],
1309            ),
1310            (
1311                "$[1:4].id",
1312                vec![PathElement::Index(0), PathElement::Member("id".into())],
1313            ),
1314            (
1315                "$[1].id",
1316                vec![PathElement::Index(2), PathElement::Member("id".into())],
1317            ),
1318            ("$[1:4].id", vec![PathElement::Index(1)]),
1319            (
1320                "$[1,\"a\"].id",
1321                vec![PathElement::Index(2), PathElement::Member("id".into())],
1322            ),
1323            ("$[1,\"a\"].id", vec![PathElement::Member("a".into())]),
1324            ("$..author.id", vec![PathElement::Member("author".into())]),
1325            ("$..*.author", vec![PathElement::Member("author".into())]),
1326            (
1327                "$..[1].id",
1328                vec![PathElement::Index(2), PathElement::Member("id".into())],
1329            ),
1330            ("$..[1].id", vec![PathElement::Index(1)]),
1331            (
1332                "$..[1:4].id",
1333                vec![PathElement::Index(0), PathElement::Member("id".into())],
1334            ),
1335            (
1336                "$..[1,\"a\"].id",
1337                vec![PathElement::Index(2), PathElement::Member("id".into())],
1338            ),
1339        ];
1340
1341        for (selector, concrete) in cases {
1342            assert!(
1343                !selector
1344                    .parse::<JsonPath>()
1345                    .unwrap()
1346                    .matches_path(&concrete),
1347                "{selector} unexpectedly matched {concrete:?}"
1348            );
1349        }
1350    }
1351
1352    #[test]
1353    fn unions_match_each_child_selector_kind() {
1354        let cases = [
1355            (
1356                "$.a[\"foo\",9].id".parse::<JsonPath>().unwrap(),
1357                vec![
1358                    PathElement::Member("a".into()),
1359                    PathElement::Member("foo".into()),
1360                    PathElement::Member("id".into()),
1361                ],
1362            ),
1363            (
1364                "$.a[1:4,9].id".parse::<JsonPath>().unwrap(),
1365                vec![
1366                    PathElement::Member("a".into()),
1367                    PathElement::Index(2),
1368                    PathElement::Member("id".into()),
1369                ],
1370            ),
1371            (
1372                "$.a[*,9].id".parse::<JsonPath>().unwrap(),
1373                vec![
1374                    PathElement::Member("a".into()),
1375                    PathElement::Member("anything".into()),
1376                    PathElement::Member("id".into()),
1377                ],
1378            ),
1379            (
1380                JsonPath::from_segments([
1381                    Segment::Member("a".into()),
1382                    Segment::Union(
1383                        vec![
1384                            Segment::Union(
1385                                vec![Segment::Member("foo".into()), Segment::Index(4)]
1386                                    .into_boxed_slice(),
1387                            ),
1388                            Segment::Index(9),
1389                        ]
1390                        .into_boxed_slice(),
1391                    ),
1392                    Segment::Member("id".into()),
1393                ]),
1394                vec![
1395                    PathElement::Member("a".into()),
1396                    PathElement::Member("foo".into()),
1397                    PathElement::Member("id".into()),
1398                ],
1399            ),
1400        ];
1401
1402        for (selector, concrete) in cases {
1403            assert!(
1404                selector.matches_path(&concrete),
1405                "{selector} did not match {concrete:?}"
1406            );
1407        }
1408    }
1409
1410    #[test]
1411    fn parses_json_string_escapes_in_member_names() {
1412        let path: JsonPath = r#"$["\"\\\/\b\f\n\r\t"]"#.parse().unwrap();
1413        assert_eq!(
1414            path.segments(),
1415            &[Segment::Member("\"\\/\u{8}\u{c}\n\r\t".into())]
1416        );
1417
1418        let path: JsonPath = r#"$['\'']"#.parse().unwrap();
1419        assert_eq!(path.segments(), &[Segment::Member("'".into())]);
1420
1421        let path: JsonPath = r#"$["\uD834\uDD1E"]"#.parse().unwrap();
1422        assert_eq!(path.segments(), &[Segment::Member("𝄞".into())]);
1423    }
1424
1425    #[test]
1426    fn rejects_malformed_jsonpath_syntax() {
1427        let cases = [
1428            (
1429                "$[x]",
1430                JsonErrorKind::InvalidJsonPath("invalid bracket selector"),
1431            ),
1432            (
1433                "$[1:x]",
1434                JsonErrorKind::InvalidJsonPath("unexpected character"),
1435            ),
1436            (
1437                "$.0bad",
1438                JsonErrorKind::InvalidJsonPath("expected member name"),
1439            ),
1440            (
1441                r#"$["\uD834x"]"#,
1442                JsonErrorKind::InvalidJsonPath("invalid unicode surrogate pair"),
1443            ),
1444            (
1445                r#"$["\uDD1E"]"#,
1446                JsonErrorKind::InvalidJsonPath("invalid unicode surrogate pair"),
1447            ),
1448            (
1449                r#"$["\u12x4"]"#,
1450                JsonErrorKind::InvalidJsonPath("invalid unicode escape"),
1451            ),
1452            (
1453                "$[::x]",
1454                JsonErrorKind::InvalidJsonPath("unexpected character"),
1455            ),
1456        ];
1457
1458        for (input, expected) in cases {
1459            let err = input.parse::<JsonPath>().unwrap_err();
1460            assert_eq!(err.kind(), &expected, "{input}");
1461        }
1462    }
1463
1464    #[test]
1465    fn deterministic_path_fuzz_roundtrips_and_matches_consistently() {
1466        let mut seed = 0x4d59_5df4_d0f3_3173;
1467        for _ in 0..2048 {
1468            let path = fuzz_path(&mut seed);
1469            let rendered = path.to_string();
1470            let reparsed: JsonPath = rendered.parse().unwrap();
1471            assert_eq!(path, reparsed, "{rendered}");
1472
1473            let concrete = fuzz_value_path(&mut seed);
1474            assert_eq!(
1475                path.matches_path(&concrete),
1476                reparsed.matches_path(&concrete),
1477                "{rendered} vs {concrete:?}"
1478            );
1479        }
1480    }
1481
1482    fn fuzz_path(seed: &mut u64) -> JsonPath {
1483        let mut segments = Vec::new();
1484        let len = (next(seed) % 6) as usize;
1485        for _ in 0..len {
1486            segments.push(match next(seed) % 9 {
1487                0 => Segment::Member(fuzz_member(seed).into()),
1488                1 => Segment::Index((next(seed) % 8) as usize),
1489                2 => Segment::Wildcard,
1490                3 => Segment::Slice(fuzz_slice(seed)),
1491                4 => Segment::Union(fuzz_union(seed)),
1492                5 => Segment::DescendantMember(fuzz_member(seed).into()),
1493                6 => Segment::DescendantIndex((next(seed) % 8) as usize),
1494                7 => Segment::DescendantSlice(fuzz_slice(seed)),
1495                _ => Segment::DescendantUnion(fuzz_union(seed)),
1496            });
1497        }
1498        JsonPath::from_segments(segments)
1499    }
1500
1501    fn fuzz_union(seed: &mut u64) -> Box<[Segment]> {
1502        let len = 1 + (next(seed) % 4) as usize;
1503        (0..len)
1504            .map(|_| match next(seed) % 4 {
1505                0 => Segment::Member(fuzz_member(seed).into()),
1506                1 => Segment::Index((next(seed) % 8) as usize),
1507                2 => Segment::Wildcard,
1508                _ => Segment::Slice(fuzz_slice(seed)),
1509            })
1510            .collect()
1511    }
1512
1513    fn fuzz_slice(seed: &mut u64) -> Slice {
1514        let start = next(seed)
1515            .is_multiple_of(2)
1516            .then(|| (next(seed) % 5) as usize);
1517        let width = (next(seed) % 6) as usize;
1518        let end = start.map(|start| start + width);
1519        let step = 1 + (next(seed) % 4) as usize;
1520        Slice::new(start, end, step)
1521    }
1522
1523    fn fuzz_member(seed: &mut u64) -> &'static str {
1524        match next(seed) % 6 {
1525            0 => "a",
1526            1 => "b",
1527            2 => "book",
1528            3 => "author",
1529            4 => "weird.key",
1530            _ => "line\nbreak",
1531        }
1532    }
1533
1534    fn fuzz_value_path(seed: &mut u64) -> Vec<PathElement> {
1535        let len = (next(seed) % 6) as usize;
1536        (0..len)
1537            .map(|_| {
1538                if next(seed).is_multiple_of(2) {
1539                    PathElement::Member(fuzz_member(seed).into())
1540                } else {
1541                    PathElement::Index((next(seed) % 8) as usize)
1542                }
1543            })
1544            .collect()
1545    }
1546
1547    fn next(seed: &mut u64) -> u64 {
1548        *seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
1549        *seed
1550    }
1551}