Skip to main content

rich_ext/data/
select.rs

1//! Pluggable selection expressions.
2//!
3//! A [`SelectorBackend`] compiles an expression into a [`Selector`], which
4//! picks nodes out of a document. [`Selectors`] is a small registry of
5//! backends by name, so a tool can offer `--select jsonpath:…` today and
6//! other languages later. With the `jsonpath` feature the registry starts
7//! with the built-in `JsonPath` backend.
8
9use std::fmt;
10
11use super::{Node, Path};
12
13/// A compile or evaluation failure.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct SelectError {
16    pub message: String,
17    /// The 1-based character column of the offending character.
18    pub column: Option<usize>,
19}
20
21impl SelectError {
22    pub fn new(message: impl Into<String>, column: Option<usize>) -> Self {
23        SelectError {
24            message: message.into(),
25            column,
26        }
27    }
28}
29
30impl fmt::Display for SelectError {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        match self.column {
33            Some(column) => write!(f, "{} at column {column}", self.message),
34            None => f.write_str(&self.message),
35        }
36    }
37}
38
39impl std::error::Error for SelectError {}
40
41/// A compiled expression.
42pub trait Selector {
43    /// The selected nodes with their paths, in the expression's order.
44    fn select<'a>(&self, root: &'a Node) -> Result<Vec<(Path, &'a Node)>, SelectError>;
45}
46
47/// An expression language.
48pub trait SelectorBackend {
49    /// The name it registers under (`jsonpath`).
50    fn name(&self) -> &str;
51    /// Compile `expr`.
52    fn compile(&self, expr: &str) -> Result<Box<dyn Selector>, SelectError>;
53}
54
55/// Backends by name.
56///
57/// ```
58/// use rich_ext::data::{parse, Format, Selectors};
59///
60/// let node = parse(Format::Json, r#"{"servers": [{"port": 80}, {"port": 443}]}"#).unwrap();
61/// # #[cfg(feature = "jsonpath")] {
62/// let selector = Selectors::default().compile("jsonpath", "$.servers[*].port").unwrap();
63/// let ports: Vec<String> = selector.select(&node).unwrap().iter().map(|(p, _)| p.to_string()).collect();
64/// assert_eq!(ports, ["servers[0].port", "servers[1].port"]);
65/// # }
66/// ```
67pub struct Selectors {
68    backends: Vec<Box<dyn SelectorBackend>>,
69}
70
71impl Default for Selectors {
72    /// The built-in backends: `JsonPath` with the `jsonpath` feature,
73    /// none otherwise.
74    fn default() -> Self {
75        #[allow(unused_mut)]
76        let mut selectors = Selectors::new();
77        #[cfg(feature = "jsonpath")]
78        selectors.register(Box::new(JsonPath));
79        selectors
80    }
81}
82
83impl Selectors {
84    /// An empty registry.
85    pub fn new() -> Self {
86        Selectors {
87            backends: Vec::new(),
88        }
89    }
90
91    /// Add a backend, replacing any with the same name.
92    pub fn register(&mut self, backend: Box<dyn SelectorBackend>) -> &mut Self {
93        self.backends.retain(|b| b.name() != backend.name());
94        self.backends.push(backend);
95        self
96    }
97
98    /// The backend called `name`.
99    pub fn get(&self, name: &str) -> Option<&dyn SelectorBackend> {
100        self.backends
101            .iter()
102            .find(|b| b.name() == name)
103            .map(|b| &**b)
104    }
105
106    /// Registered names, in registration order.
107    pub fn names(&self) -> Vec<&str> {
108        self.backends.iter().map(|b| b.name()).collect()
109    }
110
111    /// Compile `expr` with the backend called `backend`.
112    pub fn compile(&self, backend: &str, expr: &str) -> Result<Box<dyn Selector>, SelectError> {
113        match self.get(backend) {
114            Some(b) => b.compile(expr),
115            None => Err(SelectError::new(
116                format!("no selector backend named `{backend}`"),
117                None,
118            )),
119        }
120    }
121}
122
123#[cfg(feature = "jsonpath")]
124pub use jsonpath::{JsonPath, JsonPathSelector};
125
126#[cfg(feature = "jsonpath")]
127mod jsonpath {
128    use std::cmp::Ordering;
129
130    use super::{SelectError, Selector, SelectorBackend};
131    use crate::data::{value_eq, Node, Path, PathSegment, Value};
132
133    /// The built-in JSONPath backend (`jsonpath`).
134    ///
135    /// Supported: `$`, `.key`, `['key']` / `["key"]`, `[n]`, `[-n]`, `[*]`,
136    /// `.*`, `..key` / `..*` / `..[…]` (recursive descent), slices
137    /// `[a:b]` / `[a:b:step]`, unions `[0,2]` / `['a','b']`, and filters
138    /// `[?(@.k == v)]`, `[?(@.k)]` (existence), `[?@.k > 1]` with `==`,
139    /// `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!` and parentheses, comparing
140    /// numbers, strings, booleans and `null`. Filter paths may start at `@`
141    /// (the candidate) or `$` (the root). The leading `$` may be omitted:
142    /// `servers[0].name` means `$.servers[0].name`.
143    ///
144    /// ```
145    /// use rich_ext::data::{parse, Format, JsonPathSelector, Selector};
146    ///
147    /// let node = parse(Format::Json, r#"{"items": [{"n": 1}, {"n": 5}, {"n": 9}]}"#).unwrap();
148    /// let selector = JsonPathSelector::parse("$.items[?(@.n > 2)].n").unwrap();
149    /// let found: Vec<_> = selector.select(&node).unwrap().into_iter().map(|(p, _)| p.to_string()).collect();
150    /// assert_eq!(found, ["items[1].n", "items[2].n"]);
151    ///
152    /// let error = JsonPathSelector::parse("$.items[1").unwrap_err();
153    /// assert_eq!(error.to_string(), "expected `]` at column 10");
154    /// ```
155    #[derive(Clone, Copy, Debug, Default)]
156    pub struct JsonPath;
157
158    impl SelectorBackend for JsonPath {
159        fn name(&self) -> &str {
160            "jsonpath"
161        }
162        fn compile(&self, expr: &str) -> Result<Box<dyn Selector>, SelectError> {
163            Ok(Box::new(JsonPathSelector::parse(expr)?))
164        }
165    }
166
167    /// A compiled JSONPath expression.
168    #[derive(Clone, Debug)]
169    pub struct JsonPathSelector {
170        steps: Vec<Step>,
171    }
172
173    #[derive(Clone, Debug)]
174    struct Step {
175        descendant: bool,
176        selectors: Vec<Sel>,
177    }
178
179    #[derive(Clone, Debug)]
180    enum Sel {
181        Name(String),
182        Wildcard,
183        Index(i64),
184        Slice(Option<i64>, Option<i64>, i64),
185        Filter(Box<Expr>),
186    }
187
188    /// How deeply `!` and parentheses may nest in a filter. Parsing and
189    /// evaluation recurse per level, so the limit keeps hostile expressions
190    /// off the end of the stack. (`||` and `&&` chains are flat lists and
191    /// cost no depth.)
192    const MAX_FILTER_DEPTH: usize = 128;
193
194    /// How many nodes one selection may produce (or visit through
195    /// recursive descent) before it stops with an error: each carries its
196    /// own path, and chained `..*` steps multiply.
197    const MAX_SELECTED: usize = 1_000_000;
198
199    #[derive(Clone, Debug)]
200    enum Expr {
201        Or(Vec<Expr>),
202        And(Vec<Expr>),
203        Not(Box<Expr>),
204        Compare(Operand, Op, Operand),
205        Exists(Operand),
206    }
207
208    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
209    enum Op {
210        Eq,
211        Ne,
212        Lt,
213        Le,
214        Gt,
215        Ge,
216    }
217
218    #[derive(Clone, Debug)]
219    enum Operand {
220        Current(Vec<PathSegment>, Vec<i64>),
221        Root(Vec<PathSegment>, Vec<i64>),
222        Literal(Node),
223    }
224
225    struct Parser {
226        chars: Vec<char>,
227        pos: usize,
228        /// Current `!` / parenthesis nesting inside a filter.
229        depth: usize,
230    }
231
232    fn is_name_char(c: char) -> bool {
233        !c.is_whitespace() && !".[]()?,=!<>&|'\"*$:".contains(c)
234    }
235
236    impl Parser {
237        fn error<T>(&self, message: impl Into<String>) -> Result<T, SelectError> {
238            Err(SelectError::new(message, Some(self.pos + 1)))
239        }
240        fn peek(&self) -> Option<char> {
241            self.chars.get(self.pos).copied()
242        }
243        fn peek_at(&self, offset: usize) -> Option<char> {
244            self.chars.get(self.pos + offset).copied()
245        }
246        fn eat(&mut self, c: char) -> bool {
247            if self.peek() == Some(c) {
248                self.pos += 1;
249                true
250            } else {
251                false
252            }
253        }
254        fn expect(&mut self, c: char) -> Result<(), SelectError> {
255            if self.eat(c) {
256                Ok(())
257            } else {
258                self.error(format!("expected `{c}`"))
259            }
260        }
261        fn blanks(&mut self) {
262            while self.peek().is_some_and(char::is_whitespace) {
263                self.pos += 1;
264            }
265        }
266
267        fn name(&mut self) -> Result<String, SelectError> {
268            let start = self.pos;
269            while self.peek().is_some_and(is_name_char) {
270                self.pos += 1;
271            }
272            if start == self.pos {
273                return match self.peek() {
274                    Some(c) => self.error(format!("unexpected `{c}`, expected a key")),
275                    None => self.error("expected a key"),
276                };
277            }
278            Ok(self.chars[start..self.pos].iter().collect())
279        }
280
281        fn string(&mut self) -> Result<String, SelectError> {
282            let quote = self.peek().expect("called at a quote");
283            let open = self.pos;
284            self.pos += 1;
285            let mut out = String::new();
286            loop {
287                match self.peek() {
288                    None => {
289                        self.pos = open;
290                        return self.error("unterminated string");
291                    }
292                    Some(c) if c == quote => {
293                        self.pos += 1;
294                        return Ok(out);
295                    }
296                    Some('\\') => {
297                        self.pos += 1;
298                        let escaped = match self.peek() {
299                            Some('n') => '\n',
300                            Some('t') => '\t',
301                            Some('r') => '\r',
302                            Some(c @ ('\\' | '\'' | '"' | '/')) => c,
303                            Some(c) => return self.error(format!("unknown escape `\\{c}`")),
304                            None => return self.error("unterminated string"),
305                        };
306                        out.push(escaped);
307                        self.pos += 1;
308                    }
309                    Some(c) => {
310                        out.push(c);
311                        self.pos += 1;
312                    }
313                }
314            }
315        }
316
317        fn integer(&mut self) -> Result<i64, SelectError> {
318            let start = self.pos;
319            if self.peek() == Some('-') {
320                self.pos += 1;
321            }
322            while self.peek().is_some_and(|c| c.is_ascii_digit()) {
323                self.pos += 1;
324            }
325            let text: String = self.chars[start..self.pos].iter().collect();
326            text.parse().or_else(|_| {
327                self.pos = start;
328                self.error("expected an integer")
329            })
330        }
331
332        fn path(&mut self) -> Result<Vec<Step>, SelectError> {
333            let mut steps = Vec::new();
334            self.blanks();
335            if !self.eat('$') && self.peek().is_some_and(is_name_char) {
336                steps.push(Step {
337                    descendant: false,
338                    selectors: vec![Sel::Name(self.name()?)],
339                });
340            }
341            loop {
342                match self.peek() {
343                    None => break,
344                    Some('.') if self.peek_at(1) == Some('.') => {
345                        self.pos += 2;
346                        let selectors = match self.peek() {
347                            Some('[') => self.bracket()?,
348                            Some('*') => {
349                                self.pos += 1;
350                                vec![Sel::Wildcard]
351                            }
352                            _ => vec![Sel::Name(self.name()?)],
353                        };
354                        steps.push(Step {
355                            descendant: true,
356                            selectors,
357                        });
358                    }
359                    Some('.') => {
360                        self.pos += 1;
361                        let selectors = if self.eat('*') {
362                            vec![Sel::Wildcard]
363                        } else {
364                            vec![Sel::Name(self.name()?)]
365                        };
366                        steps.push(Step {
367                            descendant: false,
368                            selectors,
369                        });
370                    }
371                    Some('[') => {
372                        let selectors = self.bracket()?;
373                        steps.push(Step {
374                            descendant: false,
375                            selectors,
376                        });
377                    }
378                    Some(c) if c.is_whitespace() => {
379                        self.blanks();
380                        if self.peek().is_some() {
381                            return self.error("unexpected text after the path");
382                        }
383                    }
384                    Some(c) => return self.error(format!("unexpected `{c}`")),
385                }
386            }
387            Ok(steps)
388        }
389
390        /// `[ … ]`: a wildcard, a filter, or a union of names, indexes and
391        /// slices.
392        fn bracket(&mut self) -> Result<Vec<Sel>, SelectError> {
393            self.expect('[')?;
394            self.blanks();
395            let mut selectors = Vec::new();
396            loop {
397                self.blanks();
398                match self.peek() {
399                    Some('*') => {
400                        self.pos += 1;
401                        selectors.push(Sel::Wildcard);
402                    }
403                    Some('?') => {
404                        self.pos += 1;
405                        self.blanks();
406                        selectors.push(Sel::Filter(Box::new(self.or()?)));
407                    }
408                    Some('\'' | '"') => selectors.push(Sel::Name(self.string()?)),
409                    Some(c) if c == '-' || c == ':' || c.is_ascii_digit() => {
410                        selectors.push(self.index_or_slice()?);
411                    }
412                    Some(c) => return self.error(format!("unexpected `{c}` in brackets")),
413                    None => return self.error("expected `]`"),
414                }
415                self.blanks();
416                if !self.eat(',') {
417                    break;
418                }
419            }
420            self.expect(']')?;
421            Ok(selectors)
422        }
423
424        fn index_or_slice(&mut self) -> Result<Sel, SelectError> {
425            let bound = |p: &mut Parser| -> Result<Option<i64>, SelectError> {
426                p.blanks();
427                if p.peek().is_some_and(|c| c == '-' || c.is_ascii_digit()) {
428                    p.integer().map(Some)
429                } else {
430                    Ok(None)
431                }
432            };
433            let start = bound(self)?;
434            self.blanks();
435            if !self.eat(':') {
436                return match start {
437                    Some(index) => Ok(Sel::Index(index)),
438                    None => self.error("expected an index"),
439                };
440            }
441            let end = bound(self)?;
442            self.blanks();
443            let step = if self.eat(':') {
444                let at = self.pos;
445                match bound(self)? {
446                    Some(0) => {
447                        self.pos = at;
448                        return self.error("slice step cannot be 0");
449                    }
450                    Some(step) => step,
451                    None => 1,
452                }
453            } else {
454                1
455            };
456            Ok(Sel::Slice(start, end, step))
457        }
458
459        fn or(&mut self) -> Result<Expr, SelectError> {
460            let mut terms = vec![self.and()?];
461            loop {
462                self.blanks();
463                if self.peek() == Some('|') && self.peek_at(1) == Some('|') {
464                    self.pos += 2;
465                    terms.push(self.and()?);
466                } else if terms.len() == 1 {
467                    return Ok(terms.pop().expect("one term"));
468                } else {
469                    return Ok(Expr::Or(terms));
470                }
471            }
472        }
473
474        fn and(&mut self) -> Result<Expr, SelectError> {
475            let mut terms = vec![self.unary()?];
476            loop {
477                self.blanks();
478                if self.peek() == Some('&') && self.peek_at(1) == Some('&') {
479                    self.pos += 2;
480                    terms.push(self.unary()?);
481                } else if terms.len() == 1 {
482                    return Ok(terms.pop().expect("one term"));
483                } else {
484                    return Ok(Expr::And(terms));
485                }
486            }
487        }
488
489        /// Enter one level of `!` or parentheses.
490        fn nest(&mut self) -> Result<(), SelectError> {
491            if self.depth >= MAX_FILTER_DEPTH {
492                return self.error(format!(
493                    "filter nested too deeply (more than {MAX_FILTER_DEPTH} levels)"
494                ));
495            }
496            self.depth += 1;
497            Ok(())
498        }
499
500        fn unary(&mut self) -> Result<Expr, SelectError> {
501            self.blanks();
502            if self.peek() == Some('!') && self.peek_at(1) != Some('=') {
503                self.nest()?;
504                self.pos += 1;
505                let inner = self.unary()?;
506                self.depth -= 1;
507                return Ok(Expr::Not(Box::new(inner)));
508            }
509            if self.peek() == Some('(') {
510                self.nest()?;
511                self.pos += 1;
512                let inner = self.or()?;
513                self.blanks();
514                self.expect(')')?;
515                self.depth -= 1;
516                return Ok(inner);
517            }
518            let left = self.operand()?;
519            self.blanks();
520            let op = match (self.peek(), self.peek_at(1)) {
521                (Some('='), Some('=')) => Some((Op::Eq, 2)),
522                (Some('!'), Some('=')) => Some((Op::Ne, 2)),
523                (Some('<'), Some('=')) => Some((Op::Le, 2)),
524                (Some('>'), Some('=')) => Some((Op::Ge, 2)),
525                (Some('<'), _) => Some((Op::Lt, 1)),
526                (Some('>'), _) => Some((Op::Gt, 1)),
527                (Some('='), _) => return self.error("expected `==`"),
528                _ => None,
529            };
530            let Some((op, width)) = op else {
531                return Ok(Expr::Exists(left));
532            };
533            self.pos += width;
534            self.blanks();
535            let right = self.operand()?;
536            Ok(Expr::Compare(left, op, right))
537        }
538
539        /// A singular path from `@`/`$`, or a literal.
540        fn operand(&mut self) -> Result<Operand, SelectError> {
541            self.blanks();
542            match self.peek() {
543                Some(anchor @ ('@' | '$')) => {
544                    self.pos += 1;
545                    let mut keys = Vec::new();
546                    let mut negatives = Vec::new();
547                    loop {
548                        match self.peek() {
549                            Some('.') if self.peek_at(1) != Some('.') => {
550                                self.pos += 1;
551                                keys.push(PathSegment::Key(self.name()?));
552                            }
553                            Some('[') => {
554                                self.pos += 1;
555                                self.blanks();
556                                match self.peek() {
557                                    Some('\'' | '"') => keys.push(PathSegment::Key(self.string()?)),
558                                    _ => {
559                                        let index = self.integer()?;
560                                        if index < 0 {
561                                            negatives.push(keys.len() as i64);
562                                        }
563                                        // `unsigned_abs` fits even `i64::MIN`.
564                                        let magnitude = usize::try_from(index.unsigned_abs())
565                                            .unwrap_or(usize::MAX);
566                                        keys.push(PathSegment::Index(magnitude));
567                                    }
568                                }
569                                self.blanks();
570                                self.expect(']')?;
571                            }
572                            _ => break,
573                        }
574                    }
575                    Ok(if anchor == '@' {
576                        Operand::Current(keys, negatives)
577                    } else {
578                        Operand::Root(keys, negatives)
579                    })
580                }
581                Some('\'' | '"') => Ok(Operand::Literal(Node::new(Value::String(self.string()?)))),
582                Some(c) if c == '-' || c.is_ascii_digit() => {
583                    let start = self.pos;
584                    self.pos += 1;
585                    while self
586                        .peek()
587                        .is_some_and(|c| c.is_ascii_digit() || ".eE+-".contains(c))
588                    {
589                        self.pos += 1;
590                    }
591                    let text: String = self.chars[start..self.pos].iter().collect();
592                    let value = if let Ok(i) = text.parse::<i64>() {
593                        Value::Int(i)
594                    } else if let Ok(f) = text.parse::<f64>() {
595                        Value::Float(f)
596                    } else {
597                        self.pos = start;
598                        return self.error(format!("invalid number `{text}`"));
599                    };
600                    Ok(Operand::Literal(Node::new(value)))
601                }
602                Some(c) if c.is_ascii_alphabetic() => {
603                    let start = self.pos;
604                    while self.peek().is_some_and(|c| c.is_ascii_alphabetic()) {
605                        self.pos += 1;
606                    }
607                    let word: String = self.chars[start..self.pos].iter().collect();
608                    let value = match word.as_str() {
609                        "true" => Value::Bool(true),
610                        "false" => Value::Bool(false),
611                        "null" => Value::Null,
612                        _ => {
613                            self.pos = start;
614                            return self.error(format!(
615                                "unexpected `{word}`; expected `@`, `$` or a literal"
616                            ));
617                        }
618                    };
619                    Ok(Operand::Literal(Node::new(value)))
620                }
621                Some(c) => self.error(format!("unexpected `{c}` in filter")),
622                None => self.error("unexpected end of filter"),
623            }
624        }
625    }
626
627    impl JsonPathSelector {
628        /// Compile `expr`.
629        pub fn parse(expr: &str) -> Result<Self, SelectError> {
630            let mut parser = Parser {
631                chars: expr.chars().collect(),
632                pos: 0,
633                depth: 0,
634            };
635            if parser.chars.iter().all(|c| c.is_whitespace()) {
636                return parser.error("empty expression");
637            }
638            Ok(JsonPathSelector {
639                steps: parser.path()?,
640            })
641        }
642    }
643
644    fn children<'a>(path: &Path, node: &'a Node) -> Vec<(Path, &'a Node)> {
645        match &node.value {
646            Value::Seq(items) => items
647                .iter()
648                .enumerate()
649                .map(|(i, item)| (path.child_index(i), item))
650                .collect(),
651            Value::Map(entries) => entries
652                .iter()
653                .map(|(k, v)| (path.child_key(k), v))
654                .collect(),
655            _ => Vec::new(),
656        }
657    }
658
659    fn normalize(index: i64, len: usize) -> i64 {
660        if index < 0 {
661            len as i64 + index
662        } else {
663            index
664        }
665    }
666
667    fn resolve<'a>(root: &'a Node, keys: &[PathSegment], negatives: &[i64]) -> Option<&'a Node> {
668        let mut node = root;
669        for (i, key) in keys.iter().enumerate() {
670            node = match key {
671                PathSegment::Key(k) => node.get(k)?,
672                PathSegment::Index(n) => {
673                    // A negative index counts back from the end.
674                    let n = if negatives.contains(&(i as i64)) {
675                        node.len().checked_sub(*n)?
676                    } else {
677                        *n
678                    };
679                    node.index(n)?
680                }
681            };
682        }
683        Some(node)
684    }
685
686    fn operand<'a>(operand: &'a Operand, current: &'a Node, root: &'a Node) -> Option<&'a Node> {
687        match operand {
688            Operand::Current(keys, negatives) => resolve(current, keys, negatives),
689            Operand::Root(keys, negatives) => resolve(root, keys, negatives),
690            Operand::Literal(node) => Some(node),
691        }
692    }
693
694    fn number(value: &Value) -> Option<f64> {
695        match value {
696            Value::Int(i) => Some(*i as f64),
697            Value::UInt(u) => Some(*u as f64),
698            Value::Float(f) => Some(*f),
699            _ => None,
700        }
701    }
702
703    fn order(a: &Node, b: &Node) -> Option<Ordering> {
704        match (&a.value, &b.value) {
705            (Value::Int(x), Value::Int(y)) => Some(x.cmp(y)),
706            (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
707            (x, y) => number(x)?.partial_cmp(&number(y)?),
708        }
709    }
710
711    fn compare(a: Option<&Node>, op: Op, b: Option<&Node>) -> bool {
712        let equal = match (a, b) {
713            (None, None) => true,
714            (Some(a), Some(b)) => match (number(&a.value), number(&b.value)) {
715                (Some(x), Some(y)) => order(a, b) == Some(Ordering::Equal) || x == y,
716                _ => value_eq(a, b),
717            },
718            _ => false,
719        };
720        let ordering = match (a, b) {
721            (Some(a), Some(b)) => order(a, b),
722            _ => None,
723        };
724        match op {
725            Op::Eq => equal,
726            Op::Ne => !equal,
727            Op::Lt => ordering == Some(Ordering::Less),
728            Op::Gt => ordering == Some(Ordering::Greater),
729            Op::Le => ordering == Some(Ordering::Less) || (ordering.is_some() && equal),
730            Op::Ge => ordering == Some(Ordering::Greater) || (ordering.is_some() && equal),
731        }
732    }
733
734    fn eval(expr: &Expr, current: &Node, root: &Node) -> bool {
735        match expr {
736            Expr::Or(terms) => terms.iter().any(|t| eval(t, current, root)),
737            Expr::And(terms) => terms.iter().all(|t| eval(t, current, root)),
738            Expr::Not(a) => !eval(a, current, root),
739            Expr::Exists(o) => match o {
740                Operand::Literal(node) => node.value == Value::Bool(true),
741                _ => operand(o, current, root).is_some(),
742            },
743            Expr::Compare(a, op, b) => {
744                compare(operand(a, current, root), *op, operand(b, current, root))
745            }
746        }
747    }
748
749    fn apply<'a>(
750        sel: &Sel,
751        path: &Path,
752        node: &'a Node,
753        root: &'a Node,
754        out: &mut Vec<(Path, &'a Node)>,
755    ) {
756        match sel {
757            Sel::Name(name) => {
758                if let Value::Map(entries) = &node.value {
759                    if let Some((k, v)) = entries.iter().rev().find(|(k, _)| k == name) {
760                        out.push((path.child_key(k), v));
761                    }
762                }
763            }
764            Sel::Wildcard => out.extend(children(path, node)),
765            Sel::Index(index) => {
766                if let Value::Seq(items) = &node.value {
767                    let i = normalize(*index, items.len());
768                    if let Some(item) = usize::try_from(i).ok().and_then(|i| items.get(i)) {
769                        out.push((path.child_index(i as usize), item));
770                    }
771                }
772            }
773            Sel::Slice(start, end, step) => {
774                if let Value::Seq(items) = &node.value {
775                    let len = items.len() as i64;
776                    let step = *step;
777                    let clamp = |v: i64, lo: i64, hi: i64| v.max(lo).min(hi);
778                    let mut push = |i: i64| {
779                        out.push((path.child_index(i as usize), &items[i as usize]));
780                    };
781                    if step > 0 {
782                        let lower = clamp(normalize(start.unwrap_or(0), items.len()), 0, len);
783                        let upper = clamp(normalize(end.unwrap_or(len), items.len()), 0, len);
784                        let mut i = lower;
785                        while i < upper {
786                            push(i);
787                            let Some(following) = i.checked_add(step) else {
788                                break;
789                            };
790                            i = following;
791                        }
792                    } else {
793                        let upper = clamp(
794                            normalize(start.unwrap_or(len - 1), items.len()),
795                            -1,
796                            len - 1,
797                        );
798                        let lower =
799                            clamp(normalize(end.unwrap_or(-len - 1), items.len()), -1, len - 1);
800                        let mut i = upper;
801                        while lower < i {
802                            push(i);
803                            let Some(following) = i.checked_add(step) else {
804                                break;
805                            };
806                            i = following;
807                        }
808                    }
809                }
810            }
811            Sel::Filter(expr) => {
812                for (child_path, child) in children(path, node) {
813                    if eval(expr, child, root) {
814                        out.push((child_path, child));
815                    }
816                }
817            }
818        }
819    }
820
821    fn too_many() -> SelectError {
822        SelectError::new(
823            format!("the selection matches more than {MAX_SELECTED} nodes"),
824            None,
825        )
826    }
827
828    impl Selector for JsonPathSelector {
829        fn select<'a>(&self, root: &'a Node) -> Result<Vec<(Path, &'a Node)>, SelectError> {
830            let mut current: Vec<(Path, &'a Node)> = vec![(Path::root(), root)];
831            // Nodes visited through recursive descent, over all steps.
832            let mut visited = 0usize;
833            for step in &self.steps {
834                let mut next = Vec::new();
835                for (path, node) in &current {
836                    let targets: Vec<(Path, &'a Node)> = if step.descendant {
837                        // The node and all its descendants, parents first.
838                        let mut all = Vec::new();
839                        let mut stack = vec![(path.clone(), *node)];
840                        while let Some((p, n)) = stack.pop() {
841                            visited += 1;
842                            if visited > MAX_SELECTED {
843                                return Err(too_many());
844                            }
845                            let mut kids = children(&p, n);
846                            kids.reverse();
847                            all.push((p, n));
848                            stack.extend(kids);
849                        }
850                        all
851                    } else {
852                        vec![(path.clone(), *node)]
853                    };
854                    for (target_path, target) in &targets {
855                        for sel in &step.selectors {
856                            apply(sel, target_path, target, root, &mut next);
857                        }
858                        if next.len() > MAX_SELECTED {
859                            return Err(too_many());
860                        }
861                    }
862                }
863                current = next;
864            }
865            Ok(current)
866        }
867    }
868}