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