nextest_filtering/
parsing.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Parsing for filtersets.
5//!
6//! The parsing strategy is based on the following blog post:
7//! `<https://eyalkalderon.com/blog/nom-error-recovery/>`
8//!
9//! All high level parsing functions should:
10//! - always return Ok(_)
11//! - on error:
12//!     - consume as much input as it makes sense so that we can try to resume parsing
13//!     - return an error/none variant of the expected result type
14//!     - push an error in the parsing state (in span.state)
15
16use guppy::graph::cargo::BuildPlatform;
17use miette::SourceSpan;
18use std::fmt;
19use winnow::{
20    LocatingSlice, ModalParser, Parser,
21    ascii::line_ending,
22    combinator::{alt, delimited, eof, peek, preceded, repeat, terminated, trace},
23    stream::{Location, SliceLen, Stream},
24    token::{literal, take_till},
25};
26
27mod glob;
28mod unicode_string;
29use crate::{NameMatcher, errors::*};
30pub(crate) use glob::GenericGlob;
31pub(crate) use unicode_string::DisplayParsedString;
32
33pub(crate) type Span<'a> = winnow::Stateful<LocatingSlice<&'a str>, State<'a>>;
34type Error = ();
35type PResult<T> = winnow::ModalResult<T, Error>;
36
37pub(crate) fn new_span<'a>(input: &'a str, errors: &'a mut Vec<ParseSingleError>) -> Span<'a> {
38    Span {
39        input: LocatingSlice::new(input),
40        state: State::new(errors),
41    }
42}
43
44/// A filterset that hasn't been compiled against a package graph.
45///
46/// Not part of the public API. Exposed for testing only.
47#[derive(Clone, Debug, PartialEq, Eq)]
48#[doc(hidden)]
49pub enum SetDef<S = SourceSpan> {
50    Package(NameMatcher, S),
51    Deps(NameMatcher, S),
52    Rdeps(NameMatcher, S),
53    Kind(NameMatcher, S),
54    Binary(NameMatcher, S),
55    BinaryId(NameMatcher, S),
56    Platform(BuildPlatform, S),
57    Test(NameMatcher, S),
58    Default(S),
59    All,
60    None,
61}
62
63impl SetDef {
64    #[cfg(test)]
65    fn drop_source_span(self) -> SetDef<()> {
66        match self {
67            Self::Package(matcher, _) => SetDef::Package(matcher, ()),
68            Self::Deps(matcher, _) => SetDef::Deps(matcher, ()),
69            Self::Rdeps(matcher, _) => SetDef::Rdeps(matcher, ()),
70            Self::Kind(matcher, _) => SetDef::Kind(matcher, ()),
71            Self::Binary(matcher, _) => SetDef::Binary(matcher, ()),
72            Self::BinaryId(matcher, _) => SetDef::BinaryId(matcher, ()),
73            Self::Platform(platform, _) => SetDef::Platform(platform, ()),
74            Self::Test(matcher, _) => SetDef::Test(matcher, ()),
75            Self::Default(_) => SetDef::Default(()),
76            Self::All => SetDef::All,
77            Self::None => SetDef::None,
78        }
79    }
80}
81
82impl<S> fmt::Display for SetDef<S> {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        match self {
85            Self::Package(matcher, _) => write!(f, "package({matcher})"),
86            Self::Deps(matcher, _) => write!(f, "deps({matcher})"),
87            Self::Rdeps(matcher, _) => write!(f, "rdeps({matcher})"),
88            Self::Kind(matcher, _) => write!(f, "kind({matcher})"),
89            Self::Binary(matcher, _) => write!(f, "binary({matcher})"),
90            Self::BinaryId(matcher, _) => write!(f, "binary_id({matcher})"),
91            Self::Platform(platform, _) => write!(f, "platform({platform})"),
92            Self::Test(matcher, _) => write!(f, "test({matcher})"),
93            Self::Default(_) => write!(f, "default()"),
94            Self::All => write!(f, "all()"),
95            Self::None => write!(f, "none()"),
96        }
97    }
98}
99
100/// A filterset that hasn't been compiled against a package graph.
101///
102/// XXX: explain why `S` is required (for equality checking w/tests), or replace it with its own
103/// structure.
104#[derive(Clone, Debug, PartialEq, Eq)]
105pub enum ParsedExpr<S = SourceSpan> {
106    Not(NotOperator, Box<ParsedExpr<S>>),
107    Union(OrOperator, Box<ParsedExpr<S>>, Box<ParsedExpr<S>>),
108    Intersection(AndOperator, Box<ParsedExpr<S>>, Box<ParsedExpr<S>>),
109    Difference(DifferenceOperator, Box<ParsedExpr<S>>, Box<ParsedExpr<S>>),
110    Parens(Box<ParsedExpr<S>>),
111    Set(SetDef<S>),
112}
113
114impl ParsedExpr {
115    pub fn parse(input: &str) -> Result<Self, Vec<ParseSingleError>> {
116        let mut errors = Vec::new();
117        let span = new_span(input, &mut errors);
118        match parse(span).unwrap() {
119            ExprResult::Valid(expr) => Ok(expr),
120            ExprResult::Error => Err(errors),
121        }
122    }
123
124    fn boxed(self) -> Box<Self> {
125        Box::new(self)
126    }
127
128    fn not(self, op: NotOperator) -> Self {
129        ParsedExpr::Not(op, self.boxed())
130    }
131
132    fn union(op: OrOperator, expr_1: Self, expr_2: Self) -> Self {
133        ParsedExpr::Union(op, expr_1.boxed(), expr_2.boxed())
134    }
135
136    fn intersection(op: AndOperator, expr_1: Self, expr_2: Self) -> Self {
137        ParsedExpr::Intersection(op, expr_1.boxed(), expr_2.boxed())
138    }
139
140    fn difference(op: DifferenceOperator, expr_1: Self, expr_2: Self) -> Self {
141        ParsedExpr::Difference(op, expr_1.boxed(), expr_2.boxed())
142    }
143
144    fn parens(self) -> Self {
145        ParsedExpr::Parens(self.boxed())
146    }
147
148    #[cfg(test)]
149    fn all() -> ParsedExpr {
150        ParsedExpr::Set(SetDef::All)
151    }
152
153    #[cfg(test)]
154    fn none() -> ParsedExpr {
155        ParsedExpr::Set(SetDef::None)
156    }
157
158    #[cfg(test)]
159    fn drop_source_span(self) -> ParsedExpr<()> {
160        match self {
161            Self::Not(op, expr) => ParsedExpr::Not(op, Box::new(expr.drop_source_span())),
162            Self::Union(op, a, b) => ParsedExpr::Union(
163                op,
164                Box::new(a.drop_source_span()),
165                Box::new(b.drop_source_span()),
166            ),
167            Self::Intersection(op, a, b) => ParsedExpr::Intersection(
168                op,
169                Box::new(a.drop_source_span()),
170                Box::new(b.drop_source_span()),
171            ),
172            Self::Difference(op, a, b) => ParsedExpr::Difference(
173                op,
174                Box::new(a.drop_source_span()),
175                Box::new(b.drop_source_span()),
176            ),
177            Self::Parens(a) => ParsedExpr::Parens(Box::new(a.drop_source_span())),
178            Self::Set(set) => ParsedExpr::Set(set.drop_source_span()),
179        }
180    }
181}
182
183impl<S> fmt::Display for ParsedExpr<S> {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        match self {
186            Self::Not(op, expr) => write!(f, "{op} {expr}"),
187            Self::Union(op, expr_1, expr_2) => write!(f, "{expr_1} {op} {expr_2}"),
188            Self::Intersection(op, expr_1, expr_2) => write!(f, "{expr_1} {op} {expr_2}"),
189            Self::Difference(op, expr_1, expr_2) => write!(f, "{expr_1} {op} {expr_2}"),
190            Self::Parens(expr) => write!(f, "({expr})"),
191            Self::Set(set) => write!(f, "{set}"),
192        }
193    }
194}
195
196pub(crate) enum ExprResult {
197    Valid(ParsedExpr),
198    Error,
199}
200
201impl ExprResult {
202    fn combine(self, op: impl FnOnce(ParsedExpr, ParsedExpr) -> ParsedExpr, other: Self) -> Self {
203        match (self, other) {
204            (Self::Valid(expr_1), Self::Valid(expr_2)) => Self::Valid(op(expr_1, expr_2)),
205            _ => Self::Error,
206        }
207    }
208
209    fn negate(self, op: NotOperator) -> Self {
210        match self {
211            Self::Valid(expr) => Self::Valid(expr.not(op)),
212            _ => Self::Error,
213        }
214    }
215
216    fn parens(self) -> Self {
217        match self {
218            Self::Valid(expr) => Self::Valid(expr.parens()),
219            _ => Self::Error,
220        }
221    }
222}
223
224enum SpanLength {
225    Unknown,
226    Exact(usize),
227    Offset(isize, usize),
228}
229
230fn expect_inner<'a, F, T>(
231    mut parser: F,
232    make_err: fn(SourceSpan) -> ParseSingleError,
233    limit: SpanLength,
234) -> impl ModalParser<Span<'a>, Option<T>, Error>
235where
236    F: ModalParser<Span<'a>, T, Error>,
237{
238    move |input: &mut _| match parser.parse_next(input) {
239        Ok(out) => Ok(Some(out)),
240        Err(winnow::error::ErrMode::Backtrack(_)) | Err(winnow::error::ErrMode::Cut(_)) => {
241            let fragment_start = input.current_token_start();
242            let fragment_length = input.slice_len();
243            let span = match limit {
244                SpanLength::Unknown => (fragment_start, fragment_length).into(),
245                SpanLength::Exact(x) => (fragment_start, x.min(fragment_length)).into(),
246                SpanLength::Offset(offset, x) => {
247                    // e.g. fragment_start = 5, fragment_length = 2, offset = -1, x = 3.
248                    // Here, start = 4.
249                    let effective_start = fragment_start.saturating_add_signed(offset);
250                    // end = 6.
251                    let effective_end = effective_start + fragment_length;
252                    // len = min(3, 6 - 4) = 2.
253                    let len = (effective_end - effective_start).min(x);
254                    (effective_start, len).into()
255                }
256            };
257            let err = make_err(span);
258            input.state.report_error(err);
259            Ok(None)
260        }
261        Err(err) => Err(err),
262    }
263}
264
265fn expect<'a, F, T>(
266    parser: F,
267    make_err: fn(SourceSpan) -> ParseSingleError,
268) -> impl ModalParser<Span<'a>, Option<T>, Error>
269where
270    F: ModalParser<Span<'a>, T, Error>,
271{
272    expect_inner(parser, make_err, SpanLength::Unknown)
273}
274
275fn expect_n<'a, F, T>(
276    parser: F,
277    make_err: fn(SourceSpan) -> ParseSingleError,
278    limit: SpanLength,
279) -> impl ModalParser<Span<'a>, Option<T>, Error>
280where
281    F: ModalParser<Span<'a>, T, Error>,
282{
283    expect_inner(parser, make_err, limit)
284}
285
286fn expect_char<'a>(
287    c: char,
288    make_err: fn(SourceSpan) -> ParseSingleError,
289) -> impl ModalParser<Span<'a>, Option<char>, Error> {
290    expect_inner(ws(c), make_err, SpanLength::Exact(0))
291}
292
293fn silent_expect<'a, F, T>(mut parser: F) -> impl ModalParser<Span<'a>, Option<T>, Error>
294where
295    F: ModalParser<Span<'a>, T, Error>,
296{
297    move |input: &mut _| match parser.parse_next(input) {
298        Ok(out) => Ok(Some(out)),
299        Err(winnow::error::ErrMode::Backtrack(_)) | Err(winnow::error::ErrMode::Cut(_)) => Ok(None),
300        Err(err) => Err(err),
301    }
302}
303
304fn ws<'a, T, P: ModalParser<Span<'a>, T, Error>>(
305    mut inner: P,
306) -> impl ModalParser<Span<'a>, T, Error> {
307    move |input: &mut Span<'a>| {
308        let start = input.checkpoint();
309        () = repeat(
310            0..,
311            alt((
312                // Match individual space characters.
313                ' '.void(),
314                // Match CRLF and LF line endings. This allows filters to be specified as multiline TOML
315                // strings.
316                line_ending.void(),
317            )),
318        )
319        .parse_next(input)?;
320        match inner.parse_next(input) {
321            Ok(res) => Ok(res),
322            Err(winnow::error::ErrMode::Backtrack(err)) => {
323                input.reset(&start);
324                Err(winnow::error::ErrMode::Backtrack(err))
325            }
326            Err(winnow::error::ErrMode::Cut(err)) => {
327                input.reset(&start);
328                Err(winnow::error::ErrMode::Cut(err))
329            }
330            Err(err) => Err(err),
331        }
332    }
333}
334
335// This parse will never fail
336fn parse_matcher_text<'i>(input: &mut Span<'i>) -> PResult<Option<String>> {
337    trace("parse_matcher_text", |input: &mut Span<'i>| {
338        let res = match expect(
339            unicode_string::parse_string,
340            ParseSingleError::InvalidString,
341        )
342        .parse_next(input)
343        {
344            Ok(res) => res.flatten(),
345            Err(_) => unreachable!(),
346        };
347
348        if res.as_ref().map(|s| s.is_empty()).unwrap_or(false) {
349            let start = input.current_token_start();
350            input
351                .state
352                .report_error(ParseSingleError::InvalidString((start..0).into()));
353        }
354
355        Ok(res)
356    })
357    .parse_next(input)
358}
359
360fn parse_contains_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
361    trace(
362        "parse_contains_matcher",
363        preceded('~', parse_matcher_text).map(|res: Option<String>| {
364            res.map(|value| NameMatcher::Contains {
365                value,
366                implicit: false,
367            })
368        }),
369    )
370    .parse_next(input)
371}
372
373fn parse_equal_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
374    trace(
375        "parse_equal_matcher",
376        ws(
377            preceded('=', parse_matcher_text).map(|res: Option<String>| {
378                res.map(|value| NameMatcher::Equal {
379                    value,
380                    implicit: false,
381                })
382            }),
383        ),
384    )
385    .parse_next(input)
386}
387
388fn parse_regex_inner(input: &mut Span<'_>) -> PResult<String> {
389    trace("parse_regex_inner", |input: &mut _| {
390        enum Frag<'a> {
391            Literal(&'a str),
392            Escape(char),
393        }
394
395        let parse_escape = alt((r"\/".value('/'), '\\')).map(Frag::Escape);
396        let parse_literal = take_till(1.., ('\\', '/'))
397            .verify(|s: &str| !s.is_empty())
398            .map(|s: &str| Frag::Literal(s));
399        let parse_frag = alt((parse_escape, parse_literal));
400
401        let res = repeat(0.., parse_frag)
402            .fold(String::new, |mut string, frag| {
403                match frag {
404                    Frag::Escape(c) => string.push(c),
405                    Frag::Literal(s) => string.push_str(s),
406                }
407                string
408            })
409            .parse_next(input)?;
410
411        let _ = peek('/').parse_next(input)?;
412
413        Ok(res)
414    })
415    .parse_next(input)
416}
417
418// This should match parse_regex_inner above.
419pub(crate) struct DisplayParsedRegex<'a>(pub(crate) &'a regex::Regex);
420
421impl fmt::Display for DisplayParsedRegex<'_> {
422    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423        let regex = self.0.as_str();
424        let mut escaped = false;
425        for c in regex.chars() {
426            if escaped {
427                escaped = false;
428                write!(f, "{c}")?;
429            } else if c == '\\' {
430                escaped = true;
431                write!(f, "{c}")?;
432            } else if c == '/' {
433                // '/' is the only additional escape.
434                write!(f, "\\/")?;
435            } else {
436                write!(f, "{c}")?;
437            }
438        }
439        Ok(())
440    }
441}
442
443fn parse_regex<'i>(input: &mut Span<'i>) -> PResult<Option<NameMatcher>> {
444    trace("parse_regex", |input: &mut Span<'i>| {
445        let start = input.checkpoint();
446        let res = match parse_regex_inner.parse_next(input) {
447            Ok(res) => res,
448            Err(_) => {
449                input.reset(&start);
450                match take_till::<_, _, Error>(0.., ')').parse_next(input) {
451                    Ok(_) => {
452                        let start = input.current_token_start();
453                        let err = ParseSingleError::ExpectedCloseRegex((start, 0).into());
454                        input.state.report_error(err);
455                        return Ok(None);
456                    }
457                    Err(_) => unreachable!(),
458                }
459            }
460        };
461        match regex::Regex::new(&res).map(NameMatcher::Regex) {
462            Ok(res) => Ok(Some(res)),
463            Err(_) => {
464                let end = input.checkpoint();
465
466                input.reset(&start);
467                let start = input.current_token_start();
468
469                input.reset(&end);
470                let end = input.current_token_start();
471
472                let err = ParseSingleError::invalid_regex(&res, start, end);
473                input.state.report_error(err);
474                Ok(None)
475            }
476        }
477    })
478    .parse_next(input)
479}
480
481fn parse_regex_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
482    trace(
483        "parse_regex_matcher",
484        ws(delimited('/', parse_regex, silent_expect(ws('/')))),
485    )
486    .parse_next(input)
487}
488
489fn parse_glob_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
490    trace(
491        "parse_glob_matcher",
492        ws(preceded('#', glob::parse_glob(false))),
493    )
494    .parse_next(input)
495}
496
497// This parse will never fail (because default_matcher won't)
498fn set_matcher<'a>(
499    default_matcher: DefaultMatcher,
500) -> impl ModalParser<Span<'a>, Option<NameMatcher>, Error> {
501    ws(alt((
502        parse_regex_matcher,
503        parse_glob_matcher,
504        parse_equal_matcher,
505        parse_contains_matcher,
506        default_matcher.into_parser(),
507    )))
508}
509
510fn recover_unexpected_comma<'i>(input: &mut Span<'i>) -> PResult<()> {
511    trace("recover_unexpected_comma", |input: &mut Span<'i>| {
512        let start = input.checkpoint();
513        match peek(ws(',')).parse_next(input) {
514            Ok(_) => {
515                let pos = input.current_token_start();
516                input
517                    .state
518                    .report_error(ParseSingleError::UnexpectedComma((pos..0).into()));
519                match take_till::<_, _, Error>(0.., ')').parse_next(input) {
520                    Ok(_) => Ok(()),
521                    Err(_) => unreachable!(),
522                }
523            }
524            Err(_) => {
525                input.reset(&start);
526                Ok(())
527            }
528        }
529    })
530    .parse_next(input)
531}
532
533fn nullary_set_def<'a>(
534    name: &'static str,
535    make_set: fn(SourceSpan) -> SetDef,
536) -> impl ModalParser<Span<'a>, Option<SetDef>, Error> {
537    move |i: &mut Span<'_>| {
538        let start = i.current_token_start();
539        let _ = literal(name).parse_next(i)?;
540        let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
541        let err_loc = i.current_token_start();
542        match take_till::<_, _, Error>(0.., ')').parse_next(i) {
543            Ok(res) => {
544                if !res.trim().is_empty() {
545                    let span = (err_loc, res.len()).into();
546                    let err = ParseSingleError::UnexpectedArgument(span);
547                    i.state.report_error(err);
548                }
549            }
550            Err(_) => unreachable!(),
551        };
552        let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
553        let end = i.current_token_start();
554        Ok(Some(make_set((start, end - start).into())))
555    }
556}
557
558#[derive(Copy, Clone, Debug)]
559enum DefaultMatcher {
560    // Equal is no longer used and glob is always favored.
561    Equal,
562    Contains,
563    Glob,
564}
565
566impl DefaultMatcher {
567    fn into_parser<'a>(self) -> impl ModalParser<Span<'a>, Option<NameMatcher>, Error> {
568        move |input: &mut _| match self {
569            Self::Equal => parse_matcher_text
570                .map(|res: Option<String>| res.map(NameMatcher::implicit_equal))
571                .parse_next(input),
572            Self::Contains => parse_matcher_text
573                .map(|res: Option<String>| res.map(NameMatcher::implicit_contains))
574                .parse_next(input),
575            Self::Glob => glob::parse_glob(true).parse_next(input),
576        }
577    }
578}
579
580fn unary_set_def<'a>(
581    name: &'static str,
582    default_matcher: DefaultMatcher,
583    make_set: fn(NameMatcher, SourceSpan) -> SetDef,
584) -> impl ModalParser<Span<'a>, Option<SetDef>, Error> {
585    move |i: &mut _| {
586        let _ = literal(name).parse_next(i)?;
587        let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
588        let start = i.current_token_start();
589        let res = set_matcher(default_matcher).parse_next(i)?;
590        let end = i.current_token_start();
591        recover_unexpected_comma.parse_next(i)?;
592        let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
593        Ok(res.map(|matcher| make_set(matcher, (start, end - start).into())))
594    }
595}
596
597fn platform_def(i: &mut Span<'_>) -> PResult<Option<SetDef>> {
598    let _ = "platform".parse_next(i)?;
599    let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
600    let start = i.current_token_start();
601    // Try parsing the argument as a string for better error messages.
602    let res = ws(parse_matcher_text).parse_next(i)?;
603    let end = i.current_token_start();
604    recover_unexpected_comma.parse_next(i)?;
605    let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
606
607    // The returned string will include leading and trailing whitespace.
608    let platform = match res.as_deref().map(|res| res.trim()) {
609        Some("host") => Some(BuildPlatform::Host),
610        Some("target") => Some(BuildPlatform::Target),
611        Some(_) => {
612            i.state
613                .report_error(ParseSingleError::InvalidPlatformArgument(
614                    (start, end - start).into(),
615                ));
616            None
617        }
618        None => {
619            // This was already reported above.
620            None
621        }
622    };
623    Ok(platform.map(|platform| SetDef::Platform(platform, (start, end - start).into())))
624}
625
626fn parse_set_def(input: &mut Span<'_>) -> PResult<Option<SetDef>> {
627    trace(
628        "parse_set_def",
629        ws(alt((
630            unary_set_def("package", DefaultMatcher::Glob, SetDef::Package),
631            unary_set_def("deps", DefaultMatcher::Glob, SetDef::Deps),
632            unary_set_def("rdeps", DefaultMatcher::Glob, SetDef::Rdeps),
633            unary_set_def("kind", DefaultMatcher::Equal, SetDef::Kind),
634            // binary_id must go above binary, otherwise we'll parse the opening predicate wrong.
635            unary_set_def("binary_id", DefaultMatcher::Glob, SetDef::BinaryId),
636            unary_set_def("binary", DefaultMatcher::Glob, SetDef::Binary),
637            unary_set_def("test", DefaultMatcher::Contains, SetDef::Test),
638            platform_def,
639            nullary_set_def("default", SetDef::Default),
640            nullary_set_def("all", |_| SetDef::All),
641            nullary_set_def("none", |_| SetDef::None),
642        ))),
643    )
644    .parse_next(input)
645}
646
647fn expect_expr<'a, P: ModalParser<Span<'a>, ExprResult, Error>>(
648    inner: P,
649) -> impl ModalParser<Span<'a>, ExprResult, Error> {
650    expect(inner, ParseSingleError::ExpectedExpr).map(|res| res.unwrap_or(ExprResult::Error))
651}
652
653fn parse_parentheses_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
654    trace(
655        "parse_parentheses_expr",
656        delimited(
657            '(',
658            expect_expr(parse_expr),
659            expect_char(')', ParseSingleError::ExpectedCloseParenthesis),
660        )
661        .map(|expr| expr.parens()),
662    )
663    .parse_next(input)
664}
665
666fn parse_basic_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
667    trace(
668        "parse_basic_expr",
669        ws(alt((
670            parse_set_def.map(|set| {
671                set.map(|set| ExprResult::Valid(ParsedExpr::Set(set)))
672                    .unwrap_or(ExprResult::Error)
673            }),
674            parse_expr_not,
675            parse_parentheses_expr,
676        ))),
677    )
678    .parse_next(input)
679}
680
681#[derive(Clone, Copy, Debug, Eq, PartialEq)]
682#[cfg_attr(
683    any(test, feature = "internal-testing"),
684    derive(test_strategy::Arbitrary)
685)]
686pub enum NotOperator {
687    LiteralNot,
688    Exclamation,
689}
690
691impl fmt::Display for NotOperator {
692    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
693        match self {
694            NotOperator::LiteralNot => f.write_str("not"),
695            NotOperator::Exclamation => f.write_str("!"),
696        }
697    }
698}
699
700fn parse_expr_not(input: &mut Span<'_>) -> PResult<ExprResult> {
701    trace(
702        "parse_expr_not",
703        (
704            alt((
705                "not ".value(NotOperator::LiteralNot),
706                '!'.value(NotOperator::Exclamation),
707            )),
708            expect_expr(ws(parse_basic_expr)),
709        )
710            .map(|(op, expr)| expr.negate(op)),
711    )
712    .parse_next(input)
713}
714
715// ---
716
717#[derive(Clone, Copy, Debug, Eq, PartialEq)]
718#[cfg_attr(
719    any(test, feature = "internal-testing"),
720    derive(test_strategy::Arbitrary)
721)]
722pub enum OrOperator {
723    LiteralOr,
724    Pipe,
725    Plus,
726}
727
728impl fmt::Display for OrOperator {
729    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730        match self {
731            OrOperator::LiteralOr => f.write_str("or"),
732            OrOperator::Pipe => f.write_str("|"),
733            OrOperator::Plus => f.write_str("+"),
734        }
735    }
736}
737
738fn parse_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
739    trace("parse_expr", |input: &mut _| {
740        // "or" binds less tightly than "and", so parse and within or.
741        let expr = expect_expr(parse_and_or_difference_expr).parse_next(input)?;
742
743        let ops = repeat(
744            0..,
745            (parse_or_operator, expect_expr(parse_and_or_difference_expr)),
746        )
747        .fold(Vec::new, |mut ops, (op, expr)| {
748            ops.push((op, expr));
749            ops
750        })
751        .parse_next(input)?;
752
753        let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| {
754            if let Some(op) = op {
755                expr_1.combine(
756                    |expr_1, expr_2| ParsedExpr::union(op, expr_1, expr_2),
757                    expr_2,
758                )
759            } else {
760                ExprResult::Error
761            }
762        });
763
764        Ok(expr)
765    })
766    .parse_next(input)
767}
768
769fn parse_or_operator<'i>(input: &mut Span<'i>) -> PResult<Option<OrOperator>> {
770    trace(
771        "parse_or_operator",
772        ws(alt((
773            |input: &mut Span<'i>| {
774                let start = input.current_token_start();
775                // This is not a valid OR operator in this position, but catch it to provide a better
776                // experience.
777                let op = alt(("||", "OR ")).parse_next(input)?;
778                // || is not supported in filtersets: suggest using | instead.
779                let length = op.len();
780                let err = ParseSingleError::InvalidOrOperator((start, length).into());
781                input.state.report_error(err);
782                Ok(None)
783            },
784            "or ".value(Some(OrOperator::LiteralOr)),
785            '|'.value(Some(OrOperator::Pipe)),
786            '+'.value(Some(OrOperator::Plus)),
787        ))),
788    )
789    .parse_next(input)
790}
791
792// ---
793
794#[derive(Clone, Copy, Debug, Eq, PartialEq)]
795#[cfg_attr(
796    any(test, feature = "internal-testing"),
797    derive(test_strategy::Arbitrary)
798)]
799pub enum AndOperator {
800    LiteralAnd,
801    Ampersand,
802}
803
804impl fmt::Display for AndOperator {
805    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
806        match self {
807            AndOperator::LiteralAnd => f.write_str("and"),
808            AndOperator::Ampersand => f.write_str("&"),
809        }
810    }
811}
812
813#[derive(Clone, Copy, Debug, Eq, PartialEq)]
814#[cfg_attr(
815    any(test, feature = "internal-testing"),
816    derive(test_strategy::Arbitrary)
817)]
818pub enum DifferenceOperator {
819    Minus,
820}
821
822impl fmt::Display for DifferenceOperator {
823    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
824        match self {
825            DifferenceOperator::Minus => f.write_str("-"),
826        }
827    }
828}
829
830#[derive(Clone, Copy, Debug, Eq, PartialEq)]
831enum AndOrDifferenceOperator {
832    And(AndOperator),
833    Difference(DifferenceOperator),
834}
835
836fn parse_and_or_difference_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
837    trace("parse_and_or_difference_expr", |input: &mut _| {
838        let expr = expect_expr(parse_basic_expr).parse_next(input)?;
839
840        let ops = repeat(
841            0..,
842            (
843                parse_and_or_difference_operator,
844                expect_expr(parse_basic_expr),
845            ),
846        )
847        .fold(Vec::new, |mut ops, (op, expr)| {
848            ops.push((op, expr));
849            ops
850        })
851        .parse_next(input)?;
852
853        let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| match op {
854            Some(AndOrDifferenceOperator::And(op)) => expr_1.combine(
855                |expr_1, expr_2| ParsedExpr::intersection(op, expr_1, expr_2),
856                expr_2,
857            ),
858            Some(AndOrDifferenceOperator::Difference(op)) => expr_1.combine(
859                |expr_1, expr_2| ParsedExpr::difference(op, expr_1, expr_2),
860                expr_2,
861            ),
862            None => ExprResult::Error,
863        });
864
865        Ok(expr)
866    })
867    .parse_next(input)
868}
869
870fn parse_and_or_difference_operator<'i>(
871    input: &mut Span<'i>,
872) -> PResult<Option<AndOrDifferenceOperator>> {
873    trace(
874        "parse_and_or_difference_operator",
875        ws(alt((
876            |input: &mut Span<'i>| {
877                let start = input.current_token_start();
878                let op = alt(("&&", "AND ")).parse_next(input)?;
879                // && is not supported in filtersets: suggest using & instead.
880                let length = op.len();
881                let err = ParseSingleError::InvalidAndOperator((start, length).into());
882                input.state.report_error(err);
883                Ok(None)
884            },
885            "and ".value(Some(AndOrDifferenceOperator::And(AndOperator::LiteralAnd))),
886            '&'.value(Some(AndOrDifferenceOperator::And(AndOperator::Ampersand))),
887            '-'.value(Some(AndOrDifferenceOperator::Difference(
888                DifferenceOperator::Minus,
889            ))),
890        ))),
891    )
892    .parse_next(input)
893}
894
895// ---
896
897pub(crate) fn parse(input: Span<'_>) -> Result<ExprResult, winnow::error::ErrMode<Error>> {
898    let (_, expr) = terminated(
899        parse_expr,
900        expect(ws(eof), ParseSingleError::ExpectedEndOfExpression),
901    )
902    .parse_peek(input)?;
903    Ok(expr)
904}
905
906#[cfg(test)]
907mod tests {
908    use super::*;
909
910    #[track_caller]
911    fn parse_regex(input: &str) -> NameMatcher {
912        let mut errors = Vec::new();
913        let span = new_span(input, &mut errors);
914        parse_regex_matcher.parse_peek(span).unwrap().1.unwrap()
915    }
916
917    #[test]
918    fn test_parse_regex() {
919        assert_eq!(
920            NameMatcher::Regex(regex::Regex::new(r"some.*").unwrap()),
921            parse_regex(r"/some.*/")
922        );
923
924        assert_eq!(
925            NameMatcher::Regex(regex::Regex::new(r"a/a").unwrap()),
926            parse_regex(r"/a\/a/")
927        );
928
929        assert_eq!(
930            NameMatcher::Regex(regex::Regex::new(r"\w/a").unwrap()),
931            parse_regex(r"/\w\/a/")
932        );
933
934        assert_eq!(
935            NameMatcher::Regex(regex::Regex::new(r"\w\\/a").unwrap()),
936            parse_regex(r"/\w\\\/a/")
937        );
938
939        assert_eq!(
940            NameMatcher::Regex(regex::Regex::new(r"\p{Greek}\\/a").unwrap()),
941            parse_regex(r"/\p{Greek}\\\/a/")
942        );
943    }
944
945    #[track_caller]
946    fn parse_glob(input: &str) -> NameMatcher {
947        let mut errors = Vec::new();
948        let span = new_span(input, &mut errors);
949        let matcher = parse_glob_matcher
950            .parse_peek(span)
951            .unwrap_or_else(|error| {
952                panic!("for input {input}, parse_glob_matcher returned an error: {error}")
953            })
954            .1
955            .unwrap_or_else(|| {
956                panic!(
957                    "for input {input}, parse_glob_matcher returned None \
958                     (reported errors: {errors:?})"
959                )
960            });
961        if !errors.is_empty() {
962            panic!("for input {input}, parse_glob_matcher reported errors: {errors:?}");
963        }
964
965        matcher
966    }
967
968    fn make_glob_matcher(glob: &str, implicit: bool) -> NameMatcher {
969        NameMatcher::Glob {
970            glob: GenericGlob::new(glob.to_owned()).unwrap(),
971            implicit,
972        }
973    }
974
975    #[test]
976    fn test_parse_glob_matcher() {
977        #[track_caller]
978        fn assert_glob(input: &str, expected: &str) {
979            assert_eq!(
980                make_glob_matcher(expected, false),
981                parse_glob(input),
982                "expected matches actual for input {input:?}",
983            );
984        }
985
986        // Need the closing ) since that's used as the delimiter.
987        assert_glob(r"#something)", "something");
988        assert_glob(r"#something*)", "something*");
989        assert_glob(r"#something?)", "something?");
990        assert_glob(r"#something[abc])", "something[abc]");
991        assert_glob(r"#something[!abc])", "something[!abc]");
992        assert_glob(r"#something[a-c])", "something[a-c]");
993        assert_glob(r"#foobar\b)", "foobar\u{08}");
994        assert_glob(r"#foobar\\b)", "foobar\\b");
995        assert_glob(r"#foobar\))", "foobar)");
996    }
997
998    #[track_caller]
999    fn parse_set(input: &str) -> SetDef {
1000        let mut errors = Vec::new();
1001        let span = new_span(input, &mut errors);
1002        parse_set_def.parse_peek(span).unwrap().1.unwrap()
1003    }
1004
1005    macro_rules! assert_set_def {
1006        ($input: expr, $name:ident, $matches:expr) => {
1007            assert!(matches!($input, SetDef::$name(x, _) if x == $matches));
1008        };
1009    }
1010
1011    #[test]
1012    fn test_parse_name_matcher() {
1013        // Basic matchers
1014        assert_set_def!(
1015            parse_set("test(~something)"),
1016            Test,
1017            NameMatcher::Contains {
1018                value: "something".to_string(),
1019                implicit: false,
1020            }
1021        );
1022
1023        assert_set_def!(
1024            parse_set("test(=something)"),
1025            Test,
1026            NameMatcher::Equal {
1027                value: "something".to_string(),
1028                implicit: false,
1029            }
1030        );
1031        assert_set_def!(
1032            parse_set("test(/some.*/)"),
1033            Test,
1034            NameMatcher::Regex(regex::Regex::new("some.*").unwrap())
1035        );
1036        assert_set_def!(
1037            parse_set("test(#something)"),
1038            Test,
1039            make_glob_matcher("something", false)
1040        );
1041        assert_set_def!(
1042            parse_set("test(#something*)"),
1043            Test,
1044            make_glob_matcher("something*", false)
1045        );
1046        assert_set_def!(
1047            parse_set(r"test(#something/[?])"),
1048            Test,
1049            make_glob_matcher("something/[?]", false)
1050        );
1051
1052        // Default matchers
1053        assert_set_def!(
1054            parse_set("test(something)"),
1055            Test,
1056            NameMatcher::Contains {
1057                value: "something".to_string(),
1058                implicit: true,
1059            }
1060        );
1061        assert_set_def!(
1062            parse_set("package(something)"),
1063            Package,
1064            make_glob_matcher("something", true)
1065        );
1066
1067        // Explicit contains matching
1068        assert_set_def!(
1069            parse_set("test(~something)"),
1070            Test,
1071            NameMatcher::Contains {
1072                value: "something".to_string(),
1073                implicit: false,
1074            }
1075        );
1076        assert_set_def!(
1077            parse_set("test(~~something)"),
1078            Test,
1079            NameMatcher::Contains {
1080                value: "~something".to_string(),
1081                implicit: false,
1082            }
1083        );
1084        assert_set_def!(
1085            parse_set("test(~=something)"),
1086            Test,
1087            NameMatcher::Contains {
1088                value: "=something".to_string(),
1089                implicit: false,
1090            }
1091        );
1092        assert_set_def!(
1093            parse_set("test(~/something/)"),
1094            Test,
1095            NameMatcher::Contains {
1096                value: "/something/".to_string(),
1097                implicit: false,
1098            }
1099        );
1100        assert_set_def!(
1101            parse_set("test(~#something)"),
1102            Test,
1103            NameMatcher::Contains {
1104                value: "#something".to_string(),
1105                implicit: false,
1106            }
1107        );
1108
1109        // Explicit equals matching.
1110        assert_set_def!(
1111            parse_set("test(=something)"),
1112            Test,
1113            NameMatcher::Equal {
1114                value: "something".to_string(),
1115                implicit: false,
1116            }
1117        );
1118        assert_set_def!(
1119            parse_set("test(=~something)"),
1120            Test,
1121            NameMatcher::Equal {
1122                value: "~something".to_string(),
1123                implicit: false,
1124            }
1125        );
1126        assert_set_def!(
1127            parse_set("test(==something)"),
1128            Test,
1129            NameMatcher::Equal {
1130                value: "=something".to_string(),
1131                implicit: false,
1132            }
1133        );
1134        assert_set_def!(
1135            parse_set("test(=/something/)"),
1136            Test,
1137            NameMatcher::Equal {
1138                value: "/something/".to_string(),
1139                implicit: false,
1140            }
1141        );
1142        assert_set_def!(
1143            parse_set("test(=#something)"),
1144            Test,
1145            NameMatcher::Equal {
1146                value: "#something".to_string(),
1147                implicit: false,
1148            }
1149        );
1150
1151        // Explicit glob matching.
1152        assert_set_def!(
1153            parse_set("test(#~something)"),
1154            Test,
1155            make_glob_matcher("~something", false)
1156        );
1157        assert_set_def!(
1158            parse_set("test(#=something)"),
1159            Test,
1160            make_glob_matcher("=something", false)
1161        );
1162        assert_set_def!(
1163            parse_set("test(#/something/)"),
1164            Test,
1165            make_glob_matcher("/something/", false)
1166        );
1167        assert_set_def!(
1168            parse_set("test(##something)"),
1169            Test,
1170            make_glob_matcher("#something", false)
1171        );
1172    }
1173
1174    #[test]
1175    fn test_parse_name_matcher_quote() {
1176        assert_set_def!(
1177            parse_set(r"test(some'thing)"),
1178            Test,
1179            NameMatcher::Contains {
1180                value: r"some'thing".to_string(),
1181                implicit: true,
1182            }
1183        );
1184        assert_set_def!(
1185            parse_set(r"test(some(thing\))"),
1186            Test,
1187            NameMatcher::Contains {
1188                value: r"some(thing)".to_string(),
1189                implicit: true,
1190            }
1191        );
1192        assert_set_def!(
1193            parse_set(r"test(some \u{55})"),
1194            Test,
1195            NameMatcher::Contains {
1196                value: r"some U".to_string(),
1197                implicit: true,
1198            }
1199        );
1200    }
1201
1202    #[test]
1203    fn test_parse_set_def() {
1204        assert_eq!(SetDef::All, parse_set("all()"));
1205        assert_eq!(SetDef::All, parse_set(" all ( ) "));
1206
1207        assert_eq!(SetDef::None, parse_set("none()"));
1208
1209        assert_set_def!(
1210            parse_set("package(=something)"),
1211            Package,
1212            NameMatcher::Equal {
1213                value: "something".to_string(),
1214                implicit: false,
1215            }
1216        );
1217        assert_set_def!(
1218            parse_set("deps(something)"),
1219            Deps,
1220            make_glob_matcher("something", true)
1221        );
1222        assert_set_def!(
1223            parse_set("rdeps(something)"),
1224            Rdeps,
1225            make_glob_matcher("something", true)
1226        );
1227        assert_set_def!(
1228            parse_set("test(something)"),
1229            Test,
1230            NameMatcher::Contains {
1231                value: "something".to_string(),
1232                implicit: true,
1233            }
1234        );
1235        assert_set_def!(parse_set("platform(host)"), Platform, BuildPlatform::Host);
1236        assert_set_def!(
1237            parse_set("platform(target)"),
1238            Platform,
1239            BuildPlatform::Target
1240        );
1241        assert_set_def!(
1242            parse_set("platform(    host    )"),
1243            Platform,
1244            BuildPlatform::Host
1245        );
1246    }
1247
1248    #[track_caller]
1249    fn parse(input: &str) -> ParsedExpr {
1250        match ParsedExpr::parse(input) {
1251            Ok(expr) => expr,
1252            Err(errors) => {
1253                for single_error in &errors {
1254                    let report = miette::Report::new(single_error.clone())
1255                        .with_source_code(input.to_owned());
1256                    eprintln!("{report:?}");
1257                }
1258                panic!("Not a valid expression!")
1259            }
1260        }
1261    }
1262
1263    #[test]
1264    fn test_parse_expr_set() {
1265        let expr = ParsedExpr::all();
1266        assert_eq!(expr, parse("all()"));
1267        assert_eq!(expr, parse("  all ( ) "));
1268        assert_eq!(format!("{expr}"), "all()");
1269    }
1270
1271    #[test]
1272    fn test_parse_expr_not() {
1273        let expr = ParsedExpr::all().not(NotOperator::LiteralNot);
1274        assert_eq_both_ways(&expr, "not all()");
1275        assert_eq!(expr, parse("not  all()"));
1276
1277        let expr = ParsedExpr::all().not(NotOperator::Exclamation);
1278        assert_eq_both_ways(&expr, "! all()");
1279        assert_eq!(expr, parse("!all()"));
1280
1281        let expr = ParsedExpr::all()
1282            .not(NotOperator::LiteralNot)
1283            .not(NotOperator::LiteralNot);
1284        assert_eq_both_ways(&expr, "not not all()");
1285    }
1286
1287    #[test]
1288    fn test_parse_expr_intersection() {
1289        let expr = ParsedExpr::intersection(
1290            AndOperator::LiteralAnd,
1291            ParsedExpr::all(),
1292            ParsedExpr::none(),
1293        );
1294        assert_eq_both_ways(&expr, "all() and none()");
1295        assert_eq!(expr, parse("all()and none()"));
1296
1297        let expr = ParsedExpr::intersection(
1298            AndOperator::Ampersand,
1299            ParsedExpr::all(),
1300            ParsedExpr::none(),
1301        );
1302        assert_eq_both_ways(&expr, "all() & none()");
1303        assert_eq!(expr, parse("all()&none()"));
1304    }
1305
1306    #[test]
1307    fn test_parse_expr_union() {
1308        let expr = ParsedExpr::union(OrOperator::LiteralOr, ParsedExpr::all(), ParsedExpr::none());
1309        assert_eq_both_ways(&expr, "all() or none()");
1310        assert_eq!(expr, parse("all()or none()"));
1311
1312        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none());
1313        assert_eq_both_ways(&expr, "all() | none()");
1314        assert_eq!(expr, parse("all()|none()"));
1315
1316        let expr = ParsedExpr::union(OrOperator::Plus, ParsedExpr::all(), ParsedExpr::none());
1317        assert_eq_both_ways(&expr, "all() + none()");
1318        assert_eq!(expr, parse("all()+none()"));
1319    }
1320
1321    #[test]
1322    fn test_parse_expr_difference() {
1323        let expr = ParsedExpr::difference(
1324            DifferenceOperator::Minus,
1325            ParsedExpr::all(),
1326            ParsedExpr::none(),
1327        );
1328        assert_eq_both_ways(&expr, "all() - none()");
1329        assert_eq!(expr, parse("all()-none()"));
1330    }
1331
1332    #[test]
1333    fn test_parse_expr_precedence() {
1334        let expr = ParsedExpr::intersection(
1335            AndOperator::LiteralAnd,
1336            ParsedExpr::all().not(NotOperator::LiteralNot),
1337            ParsedExpr::none(),
1338        );
1339        assert_eq_both_ways(&expr, "not all() and none()");
1340
1341        let expr = ParsedExpr::intersection(
1342            AndOperator::LiteralAnd,
1343            ParsedExpr::all(),
1344            ParsedExpr::none().not(NotOperator::LiteralNot),
1345        );
1346        assert_eq_both_ways(&expr, "all() and not none()");
1347
1348        let expr = ParsedExpr::intersection(
1349            AndOperator::Ampersand,
1350            ParsedExpr::all(),
1351            ParsedExpr::none(),
1352        );
1353        let expr = ParsedExpr::union(OrOperator::Pipe, expr, ParsedExpr::all());
1354        assert_eq_both_ways(&expr, "all() & none() | all()");
1355
1356        let expr = ParsedExpr::intersection(
1357            AndOperator::Ampersand,
1358            ParsedExpr::none(),
1359            ParsedExpr::all(),
1360        );
1361        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1362        assert_eq_both_ways(&expr, "all() | none() & all()");
1363
1364        let expr =
1365            ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none()).parens();
1366        let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1367        assert_eq_both_ways(&expr, "(all() | none()) & all()");
1368
1369        let expr = ParsedExpr::intersection(
1370            AndOperator::Ampersand,
1371            ParsedExpr::none(),
1372            ParsedExpr::all(),
1373        )
1374        .parens();
1375        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1376        assert_eq_both_ways(&expr, "all() | (none() & all())");
1377
1378        let expr = ParsedExpr::difference(
1379            DifferenceOperator::Minus,
1380            ParsedExpr::all(),
1381            ParsedExpr::none(),
1382        );
1383        let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1384        assert_eq_both_ways(&expr, "all() - none() & all()");
1385
1386        let expr = ParsedExpr::intersection(
1387            AndOperator::Ampersand,
1388            ParsedExpr::all(),
1389            ParsedExpr::none(),
1390        );
1391        let expr = ParsedExpr::difference(DifferenceOperator::Minus, expr, ParsedExpr::all());
1392        assert_eq_both_ways(&expr, "all() & none() - all()");
1393
1394        let expr = ParsedExpr::intersection(
1395            AndOperator::Ampersand,
1396            ParsedExpr::none(),
1397            ParsedExpr::all(),
1398        )
1399        .parens()
1400        .not(NotOperator::LiteralNot);
1401        assert_eq_both_ways(&expr, "not (none() & all())");
1402    }
1403
1404    #[test]
1405    fn test_parse_comma() {
1406        // accept escaped comma
1407        let expr = ParsedExpr::Set(SetDef::Test(
1408            NameMatcher::Contains {
1409                value: "a,".to_string(),
1410                implicit: false,
1411            },
1412            (5, 4).into(),
1413        ));
1414        assert_eq_both_ways(&expr, r"test(~a\,)");
1415
1416        // string parsing is compatible with possible future syntax
1417        fn parse_future_syntax(
1418            input: &mut Span<'_>,
1419        ) -> PResult<(Option<NameMatcher>, Option<NameMatcher>)> {
1420            let _ = "something".parse_next(input)?;
1421            let _ = '('.parse_next(input)?;
1422            let n1 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1423            let _ = ws(',').parse_next(input)?;
1424            let n2 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1425            let _ = ')'.parse_next(input)?;
1426            Ok((n1, n2))
1427        }
1428
1429        let mut errors = Vec::new();
1430        let mut span = new_span("something(aa, bb)", &mut errors);
1431        if parse_future_syntax.parse_next(&mut span).is_err() {
1432            panic!("Failed to parse comma separated matchers");
1433        }
1434    }
1435
1436    #[track_caller]
1437    fn parse_err(input: &str) -> Vec<ParseSingleError> {
1438        let mut errors = Vec::new();
1439        let span = new_span(input, &mut errors);
1440        super::parse(span).unwrap();
1441        errors
1442    }
1443
1444    macro_rules! assert_error {
1445        ($error:ident, $name:ident, $start:literal, $end:literal) => {{
1446            let matches = matches!($error, ParseSingleError::$name(span) if span == ($start, $end).into());
1447            assert!(
1448                matches,
1449                "expected: {:?}, actual: error: {:?}",
1450                ParseSingleError::$name(($start, $end).into()),
1451                $error,
1452            );
1453        }};
1454    }
1455
1456    #[test]
1457    fn test_invalid_and_operator() {
1458        let src = "all() && none()";
1459        let mut errors = parse_err(src);
1460        assert_eq!(1, errors.len());
1461        let error = errors.remove(0);
1462        assert_error!(error, InvalidAndOperator, 6, 2);
1463
1464        let src = "all() AND none()";
1465        let mut errors = parse_err(src);
1466        assert_eq!(1, errors.len());
1467        let error = errors.remove(0);
1468        assert_error!(error, InvalidAndOperator, 6, 4);
1469    }
1470
1471    #[test]
1472    fn test_invalid_or_operator() {
1473        let src = "all() || none()";
1474        let mut errors = parse_err(src);
1475        assert_eq!(1, errors.len());
1476        let error = errors.remove(0);
1477        assert_error!(error, InvalidOrOperator, 6, 2);
1478
1479        let src = "all() OR none()";
1480        let mut errors = parse_err(src);
1481        assert_eq!(1, errors.len());
1482        let error = errors.remove(0);
1483        assert_error!(error, InvalidOrOperator, 6, 3);
1484    }
1485
1486    #[test]
1487    fn test_missing_close_parentheses() {
1488        let src = "all(";
1489        let mut errors = parse_err(src);
1490        assert_eq!(1, errors.len());
1491        let error = errors.remove(0);
1492        assert_error!(error, ExpectedCloseParenthesis, 4, 0);
1493    }
1494
1495    #[test]
1496    fn test_missing_open_parentheses() {
1497        let src = "all)";
1498        let mut errors = parse_err(src);
1499        assert_eq!(1, errors.len());
1500        let error = errors.remove(0);
1501        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1502    }
1503
1504    #[test]
1505    fn test_missing_parentheses() {
1506        let src = "all";
1507        let mut errors = parse_err(src);
1508        assert_eq!(2, errors.len());
1509        let error = errors.remove(0);
1510        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1511        let error = errors.remove(0);
1512        assert_error!(error, ExpectedCloseParenthesis, 3, 0);
1513    }
1514
1515    #[test]
1516    fn test_invalid_escapes() {
1517        let src = r"package(foobar\$\#\@baz)";
1518        let mut errors = parse_err(src);
1519        assert_eq!(3, errors.len());
1520
1521        // Ensure all three errors are reported.
1522        let error = errors.remove(0);
1523        assert_error!(error, InvalidEscapeCharacter, 14, 2);
1524
1525        let error = errors.remove(0);
1526        assert_error!(error, InvalidEscapeCharacter, 16, 2);
1527
1528        let error = errors.remove(0);
1529        assert_error!(error, InvalidEscapeCharacter, 18, 2);
1530    }
1531
1532    #[test]
1533    fn test_invalid_regex() {
1534        let src = "package(/)/)";
1535        let mut errors = parse_err(src);
1536        assert_eq!(1, errors.len());
1537        let error = errors.remove(0);
1538        assert!(
1539            matches!(error, ParseSingleError::InvalidRegex { span, .. } if span == (9, 1).into())
1540        );
1541
1542        // Ensure more detailed error messages if possible.
1543        let src = "package(/foo(ab/)";
1544        let mut errors = parse_err(src);
1545        assert_eq!(1, errors.len());
1546        let error = errors.remove(0);
1547        let (span, message) = match error {
1548            ParseSingleError::InvalidRegex { span, message } => (span, message),
1549            other => panic!("expected invalid regex with details, found {other}"),
1550        };
1551        assert_eq!(span, (12, 1).into(), "span matches");
1552        assert_eq!(message, "unclosed group");
1553    }
1554
1555    #[test]
1556    fn test_invalid_glob() {
1557        let src = "package(#)";
1558        let mut errors = parse_err(src);
1559        assert_eq!(1, errors.len());
1560        let error = errors.remove(0);
1561        assert_error!(error, InvalidString, 9, 0);
1562
1563        let src = "package(#foo[)";
1564        let mut errors = parse_err(src);
1565        assert_eq!(1, errors.len());
1566        let error = errors.remove(0);
1567        let (span, error) = match error {
1568            ParseSingleError::InvalidGlob { span, error } => (span, error),
1569            other => panic!("expected InvalidGlob with details, found {other}"),
1570        };
1571        assert_eq!(span, (9, 4).into(), "span matches");
1572        assert_eq!(error.to_string(), "unclosed character class; missing ']'");
1573    }
1574
1575    #[test]
1576    fn test_invalid_platform() {
1577        let src = "platform(foo)";
1578        let mut errors = parse_err(src);
1579        assert_eq!(1, errors.len());
1580        let error = errors.remove(0);
1581        assert_error!(error, InvalidPlatformArgument, 9, 3);
1582
1583        let src = "platform(   bar\\t)";
1584        let mut errors = parse_err(src);
1585        assert_eq!(1, errors.len());
1586        let error = errors.remove(0);
1587        assert_error!(error, InvalidPlatformArgument, 9, 8);
1588    }
1589
1590    #[test]
1591    fn test_missing_close_regex() {
1592        let src = "package(/aaa)";
1593        let mut errors = parse_err(src);
1594        assert_eq!(1, errors.len());
1595        let error = errors.remove(0);
1596        assert_error!(error, ExpectedCloseRegex, 12, 0);
1597    }
1598
1599    #[test]
1600    fn test_unexpected_argument() {
1601        let src = "all(aaa)";
1602        let mut errors = parse_err(src);
1603        assert_eq!(1, errors.len());
1604        let error = errors.remove(0);
1605        assert_error!(error, UnexpectedArgument, 4, 3);
1606    }
1607
1608    #[test]
1609    fn test_expected_expr() {
1610        let src = "all() + ";
1611        let mut errors = parse_err(src);
1612        assert_eq!(1, errors.len());
1613        let error = errors.remove(0);
1614        assert_error!(error, ExpectedExpr, 7, 1);
1615    }
1616
1617    #[test]
1618    fn test_expected_eof() {
1619        let src = "all() blabla";
1620        let mut errors = parse_err(src);
1621        assert_eq!(1, errors.len());
1622        let error = errors.remove(0);
1623        assert_error!(error, ExpectedEndOfExpression, 5, 7);
1624    }
1625
1626    #[test]
1627    fn test_missing_argument() {
1628        let src = "test()";
1629        let mut errors = parse_err(src);
1630        assert_eq!(1, errors.len());
1631        let error = errors.remove(0);
1632        assert_error!(error, InvalidString, 5, 0);
1633    }
1634
1635    #[test]
1636    fn test_unexpected_comma() {
1637        let src = "test(aa, )";
1638        let mut errors = parse_err(src);
1639        assert_eq!(1, errors.len());
1640        let error = errors.remove(0);
1641        assert_error!(error, UnexpectedComma, 7, 0);
1642    }
1643
1644    #[test]
1645    fn test_complex_error() {
1646        let src = "all) + package(/not) - deps(expr none)";
1647        let mut errors = parse_err(src);
1648        assert_eq!(2, errors.len(), "{errors:?}");
1649        let error = errors.remove(0);
1650        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1651        let error = errors.remove(0);
1652        assert_error!(error, ExpectedCloseRegex, 19, 0);
1653    }
1654
1655    #[test_strategy::proptest]
1656    fn proptest_expr_roundtrip(#[strategy(ParsedExpr::strategy())] expr: ParsedExpr<()>) {
1657        let expr_string = expr.to_string();
1658        eprintln!("expr string: {expr_string}");
1659        let expr_2 = parse(&expr_string).drop_source_span();
1660
1661        assert_eq!(expr, expr_2, "exprs must roundtrip");
1662    }
1663
1664    #[track_caller]
1665    fn assert_eq_both_ways(expr: &ParsedExpr, string: &str) {
1666        assert_eq!(expr, &parse(string));
1667        assert_eq!(format!("{expr}"), string);
1668    }
1669}