jaq_parse/
path.rs

1use super::{Delim, Token};
2use chumsky::prelude::*;
3use jaq_syn::path::{Opt, Part, Path};
4use jaq_syn::{Call, Spanned, Str};
5
6fn opt() -> impl Parser<Token, Opt, Error = Simple<Token>> + Clone {
7    just(Token::Question).or_not().map(|q| match q {
8        Some(_) => Opt::Optional,
9        None => Opt::Essential,
10    })
11}
12
13pub fn key<T, P>(expr: P) -> impl Parser<Token, Str<Spanned<T>>, Error = P::Error> + Clone
14where
15    T: From<Call<Spanned<T>>>,
16    P: Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
17{
18    select! {
19        Token::Ident(s) => Str::from(s),
20    }
21    .or(super::string::str_(expr))
22    .labelled("object key")
23}
24
25fn index<T, P>(expr: P) -> impl Parser<Token, Part<Spanned<T>>, Error = P::Error> + Clone
26where
27    T: From<Str<Spanned<T>>> + From<Call<Spanned<T>>>,
28    P: Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
29{
30    key(expr).map_with_span(|id, span| Part::Index((T::from(id), span)))
31}
32
33/// Match `[]`, `[e]`, `[e:]`, `[e:e]`, `[:e]` (all without brackets).
34fn range<T, P>(expr: P) -> impl Parser<Token, Part<Spanned<T>>, Error = P::Error> + Clone
35where
36    P: Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
37{
38    let e2 = just(Token::Colon).ignore_then(expr.clone().or_not());
39    let starts_with_expr = expr.clone().then(e2.or_not()).map(|(e1, e2)| match e2 {
40        None => Part::Index(e1),
41        Some(e2) => Part::Range(Some(e1), e2),
42    });
43    let starts_with_colon = just(Token::Colon)
44        .ignore_then(expr.clone())
45        .map(|e2| Part::Range(None, Some(e2)));
46
47    starts_with_expr
48        .or(starts_with_colon)
49        .or_not()
50        .map(|o| o.unwrap_or(Part::Range(None, None)))
51}
52
53/// A path after an atomic filter (that is not the identity filter).
54pub fn path<T, P>(expr: P) -> impl Parser<Token, Path<T>, Error = P::Error> + Clone
55where
56    T: From<Str<Spanned<T>>> + From<Call<Spanned<T>>>,
57    P: Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
58{
59    let range = Delim::Brack.around(range(expr.clone()));
60    let dot_index = just(Token::Dot).ignore_then(index(expr));
61    let dot_range = just(Token::Dot).or_not().ignore_then(range);
62    dot_index.or(dot_range).then(opt()).repeated()
63}
64
65/// The first part of a path after an identity filter.
66pub fn part<T, P>(expr: P) -> impl Parser<Token, (Part<Spanned<T>>, Opt), Error = P::Error> + Clone
67where
68    T: From<Str<Spanned<T>>> + From<Call<Spanned<T>>>,
69    P: Parser<Token, Spanned<T>, Error = Simple<Token>> + Clone,
70{
71    let range = Delim::Brack.around(range(expr.clone()));
72    range.or(index(expr)).then(opt())
73}