jsonpath_plus/
ast.rs

1//! Syntax tree that backs a path. If you just want to use paths, you shouldn't touch this.
2//! This is exposed for users who want to provide things like syntax highlighting of paths
3//! or similar.
4
5#![cfg_attr(not(feature = "spanned"), allow(dead_code))]
6
7use core::num::NonZeroI64;
8
9mod error;
10mod eval;
11mod parse;
12mod span;
13#[cfg(test)]
14mod tests;
15mod token;
16
17pub use error::{FailReason, ParseFail};
18pub use span::Span;
19#[cfg(feature = "spanned")]
20pub use span::Spanned;
21
22// Aliases
23
24type Input = char;
25type Error = ParseFail<char, ()>;
26
27// Atoms
28
29/// A raw identifier, the `foo` in `.foo`
30pub struct Ident {
31    #[cfg(feature = "spanned")]
32    span: Span,
33    val: String,
34}
35
36impl Ident {
37    /// Get the string representation of this identifier
38    #[must_use]
39    pub fn as_str(&self) -> &str {
40        &self.val
41    }
42}
43
44/// A boolean literal, such as `true` or `false`
45pub struct BoolLit {
46    #[cfg(feature = "spanned")]
47    span: Span,
48    val: bool,
49}
50
51impl BoolLit {
52    /// Get the boolean representation of this literal
53    #[must_use]
54    pub fn as_bool(&self) -> bool {
55        self.val
56    }
57}
58
59/// A null literal, the keyword `null`
60pub struct NullLit {
61    #[cfg(feature = "spanned")]
62    span: Span,
63}
64
65/// An integer literal, such as `-3`
66pub struct IntLit {
67    #[cfg(feature = "spanned")]
68    span: Span,
69    val: i64,
70}
71
72impl IntLit {
73    /// Get the integer representation of this literal
74    #[must_use]
75    pub fn as_int(&self) -> i64 {
76        self.val
77    }
78}
79
80/// A non-zero integer literal, any integer not `0`
81pub struct NonZeroIntLit {
82    #[cfg(feature = "spanned")]
83    span: Span,
84    val: NonZeroI64,
85}
86
87impl NonZeroIntLit {
88    /// Get the integer representation of this literal
89    #[must_use]
90    pub fn as_int(&self) -> NonZeroI64 {
91        self.val
92    }
93}
94
95struct StringContent {
96    #[cfg(feature = "spanned")]
97    span: Span,
98    val: String,
99}
100
101/// An apostrophe-delimited string
102pub struct SingleStringLit {
103    start: token::SingleQuote,
104    content: StringContent,
105    end: token::SingleQuote,
106}
107
108impl SingleStringLit {
109    /// Get the content of this string literal
110    #[must_use]
111    pub fn as_str(&self) -> &str {
112        &self.content.val
113    }
114}
115
116/// A quote-delimite string
117pub struct DoubleStringLit {
118    start: token::DoubleQuote,
119    content: StringContent,
120    end: token::DoubleQuote,
121}
122
123impl DoubleStringLit {
124    /// Get the content of this string literal
125    #[must_use]
126    pub fn as_str(&self) -> &str {
127        &self.content.val
128    }
129}
130
131/// Any string literal, whether single or double quote delimited
132pub enum StringLit {
133    /// A single-quoted string literal
134    Single(SingleStringLit),
135    /// A double-quoted string literal
136    Double(DoubleStringLit),
137}
138
139impl StringLit {
140    /// Get the content of this string literal
141    #[must_use]
142    pub fn as_str(&self) -> &str {
143        match self {
144            StringLit::Single(s) => s.as_str(),
145            StringLit::Double(s) => s.as_str(),
146        }
147    }
148}
149
150// Main AST
151
152/// A compiled JSON path. Can be used to match against items any number of times, preventing
153/// recompilation of the same pattern many times.
154#[must_use = "A path does nothing on its own, call `find` or `find_str` to evaluate the path on a \
155              value"]
156pub struct Path {
157    dollar: token::Dollar,
158    segments: Vec<Segment>,
159    tilde: Option<token::Tilde>,
160}
161
162impl Path {
163    /// A slice of the segments this path contains
164    #[must_use]
165    pub fn segments(&self) -> &[Segment] {
166        &self.segments
167    }
168}
169
170/// A sub-path, such as in a filter or as a bracket selector. Can be based off the root or the
171/// current location
172pub struct SubPath {
173    kind: PathKind,
174    segments: Vec<Segment>,
175    tilde: Option<token::Tilde>,
176}
177
178impl SubPath {
179    /// The kind of this sub-path
180    #[must_use]
181    pub fn kind(&self) -> &PathKind {
182        &self.kind
183    }
184
185    /// A slice of the segments this path contains
186    #[must_use]
187    pub fn segments(&self) -> &[Segment] {
188        &self.segments
189    }
190
191    /// Whether this path references the IDs of the matched items, or the items themselves
192    #[must_use]
193    pub fn is_id(&self) -> bool {
194        self.tilde.is_some()
195    }
196}
197
198/// The kind of a sub-path. Either root-based or relative
199#[non_exhaustive]
200pub enum PathKind {
201    /// A root-based path
202    Root(token::Dollar),
203    /// A relative path
204    Relative(token::At),
205}
206
207impl PathKind {
208    /// Whether this is an absolute root based path kind
209    #[must_use]
210    pub fn is_root(&self) -> bool {
211        matches!(self, PathKind::Root(_))
212    }
213
214    /// Whether this is a relative path kind
215    #[must_use]
216    pub fn is_relative(&self) -> bool {
217        matches!(self, PathKind::Relative(_))
218    }
219}
220
221/// A single segement selector in a path
222#[non_exhaustive]
223pub enum Segment {
224    /// A dot followed by a simple selector, `.a`
225    Dot(token::Dot, RawSelector),
226    /// A bracket containing a complex selector, `[?(...)]`
227    Bracket(token::Bracket, BracketSelector),
228    /// A recursive selector optionally followed by a simple selector, `..foo`
229    Recursive(token::DotDot, Option<RawSelector>),
230}
231
232/// The raw selector following a dot
233#[non_exhaustive]
234pub enum RawSelector {
235    /// A wildcard selector to get all children, `.*`
236    Wildcard(token::Star),
237    /// A parent selector to retrieve the parent of the matched item, `.^`
238    Parent(token::Caret),
239    /// A name ident selector to retrieve the matched name in an object, `.my_name`
240    Name(Ident),
241}
242
243/// A range for selecting keys from an array from a start to an end key, with an extra parameter to
244/// select every Nth key
245pub struct StepRange {
246    start: Option<IntLit>,
247    colon1: token::Colon,
248    end: Option<IntLit>,
249    colon2: token::Colon,
250    step: Option<NonZeroIntLit>,
251}
252
253impl StepRange {
254    /// Get the start literal token for this range
255    #[must_use]
256    pub fn start_lit(&self) -> Option<&IntLit> {
257        self.start.as_ref()
258    }
259
260    /// Get the end literal token for this range
261    #[must_use]
262    pub fn end_lit(&self) -> Option<&IntLit> {
263        self.end.as_ref()
264    }
265
266    /// Get the step literal token for this range
267    #[must_use]
268    pub fn step_lit(&self) -> Option<&NonZeroIntLit> {
269        self.step.as_ref()
270    }
271
272    /// Get the user-provided literal start for this range
273    #[must_use]
274    pub fn start(&self) -> Option<i64> {
275        self.start.as_ref().map(|a| a.as_int())
276    }
277
278    /// Get the user-provided literal end for this range
279    #[must_use]
280    pub fn end(&self) -> Option<i64> {
281        self.end.as_ref().map(|a| a.as_int())
282    }
283
284    /// Get the user-provided step value for this range
285    #[must_use]
286    pub fn step(&self) -> Option<NonZeroI64> {
287        self.step.as_ref().map(|a| a.as_int())
288    }
289}
290
291/// A range for selecting keys from an array from a start to an end key
292pub struct Range {
293    start: Option<IntLit>,
294    colon: token::Colon,
295    end: Option<IntLit>,
296}
297
298impl Range {
299    /// Get the start literal token for this range
300    #[must_use]
301    pub fn start_lit(&self) -> Option<&IntLit> {
302        self.start.as_ref()
303    }
304
305    /// Get the end literal token for this range
306    #[must_use]
307    pub fn end_lit(&self) -> Option<&IntLit> {
308        self.end.as_ref()
309    }
310
311    /// Get the user-provided literal start for this range
312    #[must_use]
313    pub fn start(&self) -> Option<i64> {
314        self.start.as_ref().map(|a| a.as_int())
315    }
316
317    /// Get the user-provided literal end for this range
318    #[must_use]
319    pub fn end(&self) -> Option<i64> {
320        self.end.as_ref().map(|a| a.as_int())
321    }
322}
323
324/// A component of a bracket union selector
325#[non_exhaustive]
326pub enum UnionComponent {
327    /// A range selector with explicit step
328    StepRange(StepRange),
329    /// A range selector with implicit step
330    Range(Range),
331    /// A parent selector to retrieve the parent of the matched item
332    Parent(token::Caret),
333    /// A sub-path selector to retrieve keys from a matched path
334    Path(SubPath),
335    /// A filter selector to retrieve items matching a predicate
336    Filter(Filter),
337    /// A literal selector to retrieve the mentioned keys
338    Literal(BracketLit),
339}
340
341impl TryFrom<BracketSelector> for UnionComponent {
342    type Error = ();
343
344    fn try_from(value: BracketSelector) -> Result<Self, Self::Error> {
345        Ok(match value {
346            BracketSelector::StepRange(sr) => UnionComponent::StepRange(sr),
347            BracketSelector::Range(r) => UnionComponent::Range(r),
348            BracketSelector::Parent(p) => UnionComponent::Parent(p),
349            BracketSelector::Path(p) => UnionComponent::Path(p),
350            BracketSelector::Filter(f) => UnionComponent::Filter(f),
351            BracketSelector::Literal(l) => UnionComponent::Literal(l),
352            _ => return Err(()),
353        })
354    }
355}
356
357/// The inside of a bracket selector segment
358#[non_exhaustive]
359pub enum BracketSelector {
360    /// A union of multiple selectors, `[1, 3, 9]`
361    Union(Vec<UnionComponent>),
362    /// A range selector with explicit step, `[1:5:2]`
363    StepRange(StepRange),
364    /// A range selector with implicit step, `[2:8]`
365    Range(Range),
366    /// A wildcard selector to get all children, `[*]`
367    Wildcard(token::Star),
368    /// A parent selector to retrieve the parent of the matched item, `[^]`
369    Parent(token::Caret),
370    /// A sub-path selector to retrieve keys from a matched path, `[$.foo.bar]`
371    Path(SubPath),
372    /// A filter selector to retrieve items matching a predicate, `[?(expr)]`
373    Filter(Filter),
374    /// A literal selector to retrieve the mentioned keys, `[6]` or `['qux']`
375    Literal(BracketLit),
376}
377
378/// A literal selector inside of brackets, `0` or `'a'`
379#[non_exhaustive]
380pub enum BracketLit {
381    /// An integer literal, see [`IntLit`]
382    Int(IntLit),
383    /// A string literal, see [`StringLit`]
384    String(StringLit),
385}
386
387impl BracketLit {
388    /// Whether this literal is an integer
389    #[must_use]
390    pub fn is_int(&self) -> bool {
391        matches!(self, BracketLit::Int(_))
392    }
393
394    /// Whether this literal is a string
395    #[must_use]
396    pub fn is_str(&self) -> bool {
397        matches!(self, BracketLit::String(_))
398    }
399
400    /// Get this literal as an integer value, or None
401    #[must_use]
402    pub fn as_int(&self) -> Option<i64> {
403        if let BracketLit::Int(i) = self {
404            Some(i.as_int())
405        } else {
406            None
407        }
408    }
409
410    /// Get this literal as a string value, or None
411    #[must_use]
412    pub fn as_str(&self) -> Option<&str> {
413        if let BracketLit::String(s) = self {
414            Some(s.as_str())
415        } else {
416            None
417        }
418    }
419}
420
421/// A filter selector inside of brackets, `?(...)`
422pub struct Filter {
423    question: token::Question,
424    paren: token::Paren,
425    inner: FilterExpr,
426}
427
428impl Filter {
429    /// The inner expression of this filter
430    #[must_use]
431    pub fn expression(&self) -> &FilterExpr {
432        &self.inner
433    }
434}
435
436/// A literal inside an expression
437#[non_exhaustive]
438pub enum ExprLit {
439    /// An integer literal, see [`IntLit`]
440    Int(IntLit),
441    /// A string literal, see [`StringLit`]
442    String(StringLit),
443    /// A boolean literal, see [`BoolLit`]
444    Bool(BoolLit),
445    /// A null literal, see [`NullLit`]
446    Null(NullLit),
447}
448
449impl ExprLit {
450    /// Whether this literal is an integer
451    #[must_use]
452    pub fn is_int(&self) -> bool {
453        matches!(self, ExprLit::Int(_))
454    }
455
456    /// Whether this literal is a string
457    #[must_use]
458    pub fn is_str(&self) -> bool {
459        matches!(self, ExprLit::String(_))
460    }
461
462    /// Whether this literal is a boolean
463    #[must_use]
464    pub fn is_bool(&self) -> bool {
465        matches!(self, ExprLit::Bool(_))
466    }
467
468    /// Whether this literal is a null
469    #[must_use]
470    pub fn is_null(&self) -> bool {
471        matches!(self, ExprLit::Null(_))
472    }
473
474    /// Get this literal as an integer value, or None
475    #[must_use]
476    pub fn as_int(&self) -> Option<i64> {
477        if let ExprLit::Int(i) = self {
478            Some(i.as_int())
479        } else {
480            None
481        }
482    }
483
484    /// Get this literal as a string value, or None
485    #[must_use]
486    pub fn as_str(&self) -> Option<&str> {
487        if let ExprLit::String(s) = self {
488            Some(s.as_str())
489        } else {
490            None
491        }
492    }
493
494    /// Get this literal as a boolean value, or None
495    #[must_use]
496    pub fn as_bool(&self) -> Option<bool> {
497        if let ExprLit::Bool(s) = self {
498            Some(s.as_bool())
499        } else {
500            None
501        }
502    }
503}
504
505/// An expression inside a filter directive, or any sub-expression in that tree
506#[non_exhaustive]
507pub enum FilterExpr {
508    /// An expression with an unary operator before it, such as `!(true)`
509    Unary(UnOp, Box<FilterExpr>),
510    /// Two expressions with a binary operator joining them, such as `1 + 4`
511    Binary(Box<FilterExpr>, BinOp, Box<FilterExpr>),
512    /// A [`SubPath`] expression, such as the `@.a` in `@.a == 1`
513    Path(SubPath),
514    /// A literal value, such as `null` or `'bar'`
515    Lit(ExprLit),
516    /// An expression wrapped in parenthesis, such as the `(1 + 2)` in `(1 + 2) * 3`
517    Parens(token::Paren, Box<FilterExpr>),
518}
519
520/// An unary operator in an expression
521#[non_exhaustive]
522pub enum UnOp {
523    /// `-`
524    Neg(token::Dash),
525    /// `!`
526    Not(token::Bang),
527}
528
529/// A binary operator in an expression
530#[non_exhaustive]
531pub enum BinOp {
532    /// `&&`
533    And(token::DoubleAnd),
534    /// `||`
535    Or(token::DoublePipe),
536
537    /// `==`
538    Eq(token::EqEq),
539    /// `<=`
540    Le(token::LessEq),
541    /// `<`
542    Lt(token::LessThan),
543    /// `>`
544    Gt(token::GreaterThan),
545    /// `>=`
546    Ge(token::GreaterEq),
547
548    /// `+`
549    Add(token::Plus),
550    /// `-`
551    Sub(token::Dash),
552    /// `*`
553    Mul(token::Star),
554    /// `/`
555    Div(token::RightSlash),
556    /// `%`
557    Rem(token::Percent),
558}