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            nullary_set_def("default", ParsedLeaf::Default),
670            nullary_set_def("all", |_| ParsedLeaf::All),
671            nullary_set_def("none", |_| ParsedLeaf::None),
672        ))),
673    )
674    .parse_next(input)
675}
676
677fn expect_expr<'a, P: ModalParser<Span<'a>, ExprResult, Error>>(
678    inner: P,
679) -> impl ModalParser<Span<'a>, ExprResult, Error> {
680    expect(inner, ParseSingleError::ExpectedExpr).map(|res| res.unwrap_or(ExprResult::Error))
681}
682
683fn parse_parentheses_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
684    trace(
685        "parse_parentheses_expr",
686        delimited(
687            '(',
688            expect_expr(parse_expr),
689            expect_char(')', ParseSingleError::ExpectedCloseParenthesis),
690        )
691        .map(|expr| expr.parens()),
692    )
693    .parse_next(input)
694}
695
696fn parse_basic_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
697    trace(
698        "parse_basic_expr",
699        ws(alt((
700            parse_set_def.map(|set| {
701                set.map(|set| ExprResult::Valid(ParsedExpr::Set(set)))
702                    .unwrap_or(ExprResult::Error)
703            }),
704            parse_expr_not,
705            parse_parentheses_expr,
706        ))),
707    )
708    .parse_next(input)
709}
710
711#[derive(Clone, Copy, Debug, Eq, PartialEq)]
712#[cfg_attr(
713    any(test, feature = "internal-testing"),
714    derive(test_strategy::Arbitrary)
715)]
716pub enum NotOperator {
717    LiteralNot,
718    Exclamation,
719}
720
721impl fmt::Display for NotOperator {
722    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
723        match self {
724            NotOperator::LiteralNot => f.write_str("not"),
725            NotOperator::Exclamation => f.write_str("!"),
726        }
727    }
728}
729
730fn parse_expr_not(input: &mut Span<'_>) -> PResult<ExprResult> {
731    trace(
732        "parse_expr_not",
733        (
734            alt((
735                "not ".value(NotOperator::LiteralNot),
736                '!'.value(NotOperator::Exclamation),
737            )),
738            expect_expr(ws(parse_basic_expr)),
739        )
740            .map(|(op, expr)| expr.negate(op)),
741    )
742    .parse_next(input)
743}
744
745// ---
746
747#[derive(Clone, Copy, Debug, Eq, PartialEq)]
748#[cfg_attr(
749    any(test, feature = "internal-testing"),
750    derive(test_strategy::Arbitrary)
751)]
752pub enum OrOperator {
753    LiteralOr,
754    Pipe,
755    Plus,
756}
757
758impl fmt::Display for OrOperator {
759    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
760        match self {
761            OrOperator::LiteralOr => f.write_str("or"),
762            OrOperator::Pipe => f.write_str("|"),
763            OrOperator::Plus => f.write_str("+"),
764        }
765    }
766}
767
768fn parse_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
769    trace("parse_expr", |input: &mut _| {
770        // "or" binds less tightly than "and", so parse and within or.
771        let expr = expect_expr(parse_and_or_difference_expr).parse_next(input)?;
772
773        let ops = repeat(
774            0..,
775            (parse_or_operator, expect_expr(parse_and_or_difference_expr)),
776        )
777        .fold(Vec::new, |mut ops, (op, expr)| {
778            ops.push((op, expr));
779            ops
780        })
781        .parse_next(input)?;
782
783        let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| {
784            if let Some(op) = op {
785                expr_1.combine(
786                    |expr_1, expr_2| ParsedExpr::union(op, expr_1, expr_2),
787                    expr_2,
788                )
789            } else {
790                ExprResult::Error
791            }
792        });
793
794        Ok(expr)
795    })
796    .parse_next(input)
797}
798
799fn parse_or_operator<'i>(input: &mut Span<'i>) -> PResult<Option<OrOperator>> {
800    trace(
801        "parse_or_operator",
802        ws(alt((
803            |input: &mut Span<'i>| {
804                let start = input.current_token_start();
805                // This is not a valid OR operator in this position, but catch it to provide a better
806                // experience.
807                let op = alt(("||", "OR ")).parse_next(input)?;
808                // || is not supported in filtersets: suggest using | instead.
809                let length = op.len();
810                let err = ParseSingleError::InvalidOrOperator((start, length).into());
811                input.state.report_error(err);
812                Ok(None)
813            },
814            "or ".value(Some(OrOperator::LiteralOr)),
815            '|'.value(Some(OrOperator::Pipe)),
816            '+'.value(Some(OrOperator::Plus)),
817        ))),
818    )
819    .parse_next(input)
820}
821
822// ---
823
824#[derive(Clone, Copy, Debug, Eq, PartialEq)]
825#[cfg_attr(
826    any(test, feature = "internal-testing"),
827    derive(test_strategy::Arbitrary)
828)]
829pub enum AndOperator {
830    LiteralAnd,
831    Ampersand,
832}
833
834impl fmt::Display for AndOperator {
835    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
836        match self {
837            AndOperator::LiteralAnd => f.write_str("and"),
838            AndOperator::Ampersand => f.write_str("&"),
839        }
840    }
841}
842
843#[derive(Clone, Copy, Debug, Eq, PartialEq)]
844#[cfg_attr(
845    any(test, feature = "internal-testing"),
846    derive(test_strategy::Arbitrary)
847)]
848pub enum DifferenceOperator {
849    Minus,
850}
851
852impl fmt::Display for DifferenceOperator {
853    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
854        match self {
855            DifferenceOperator::Minus => f.write_str("-"),
856        }
857    }
858}
859
860#[derive(Clone, Copy, Debug, Eq, PartialEq)]
861enum AndOrDifferenceOperator {
862    And(AndOperator),
863    Difference(DifferenceOperator),
864}
865
866fn parse_and_or_difference_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
867    trace("parse_and_or_difference_expr", |input: &mut _| {
868        let expr = expect_expr(parse_basic_expr).parse_next(input)?;
869
870        let ops = repeat(
871            0..,
872            (
873                parse_and_or_difference_operator,
874                expect_expr(parse_basic_expr),
875            ),
876        )
877        .fold(Vec::new, |mut ops, (op, expr)| {
878            ops.push((op, expr));
879            ops
880        })
881        .parse_next(input)?;
882
883        let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| match op {
884            Some(AndOrDifferenceOperator::And(op)) => expr_1.combine(
885                |expr_1, expr_2| ParsedExpr::intersection(op, expr_1, expr_2),
886                expr_2,
887            ),
888            Some(AndOrDifferenceOperator::Difference(op)) => expr_1.combine(
889                |expr_1, expr_2| ParsedExpr::difference(op, expr_1, expr_2),
890                expr_2,
891            ),
892            None => ExprResult::Error,
893        });
894
895        Ok(expr)
896    })
897    .parse_next(input)
898}
899
900fn parse_and_or_difference_operator<'i>(
901    input: &mut Span<'i>,
902) -> PResult<Option<AndOrDifferenceOperator>> {
903    trace(
904        "parse_and_or_difference_operator",
905        ws(alt((
906            |input: &mut Span<'i>| {
907                let start = input.current_token_start();
908                let op = alt(("&&", "AND ")).parse_next(input)?;
909                // && is not supported in filtersets: suggest using & instead.
910                let length = op.len();
911                let err = ParseSingleError::InvalidAndOperator((start, length).into());
912                input.state.report_error(err);
913                Ok(None)
914            },
915            "and ".value(Some(AndOrDifferenceOperator::And(AndOperator::LiteralAnd))),
916            '&'.value(Some(AndOrDifferenceOperator::And(AndOperator::Ampersand))),
917            '-'.value(Some(AndOrDifferenceOperator::Difference(
918                DifferenceOperator::Minus,
919            ))),
920        ))),
921    )
922    .parse_next(input)
923}
924
925// ---
926
927pub(crate) fn parse(input: Span<'_>) -> Result<ExprResult, winnow::error::ErrMode<Error>> {
928    let (_, expr) = terminated(
929        parse_expr,
930        expect(ws(eof), ParseSingleError::ExpectedEndOfExpression),
931    )
932    .parse_peek(input)?;
933    Ok(expr)
934}
935
936#[cfg(test)]
937mod tests {
938    use super::*;
939
940    #[track_caller]
941    fn parse_regex(input: &str) -> NameMatcher {
942        let mut errors = Vec::new();
943        let span = new_span(input, &mut errors);
944        parse_regex_matcher.parse_peek(span).unwrap().1.unwrap()
945    }
946
947    #[test]
948    fn test_parse_regex() {
949        assert_eq!(
950            NameMatcher::Regex(regex::Regex::new(r"some.*").unwrap()),
951            parse_regex(r"/some.*/")
952        );
953
954        assert_eq!(
955            NameMatcher::Regex(regex::Regex::new(r"a/a").unwrap()),
956            parse_regex(r"/a\/a/")
957        );
958
959        assert_eq!(
960            NameMatcher::Regex(regex::Regex::new(r"\w/a").unwrap()),
961            parse_regex(r"/\w\/a/")
962        );
963
964        assert_eq!(
965            NameMatcher::Regex(regex::Regex::new(r"\w\\/a").unwrap()),
966            parse_regex(r"/\w\\\/a/")
967        );
968
969        assert_eq!(
970            NameMatcher::Regex(regex::Regex::new(r"\p{Greek}\\/a").unwrap()),
971            parse_regex(r"/\p{Greek}\\\/a/")
972        );
973    }
974
975    #[track_caller]
976    fn parse_glob(input: &str) -> NameMatcher {
977        let mut errors = Vec::new();
978        let span = new_span(input, &mut errors);
979        let matcher = parse_glob_matcher
980            .parse_peek(span)
981            .unwrap_or_else(|error| {
982                panic!("for input {input}, parse_glob_matcher returned an error: {error}")
983            })
984            .1
985            .unwrap_or_else(|| {
986                panic!(
987                    "for input {input}, parse_glob_matcher returned None \
988                     (reported errors: {errors:?})"
989                )
990            });
991        if !errors.is_empty() {
992            panic!("for input {input}, parse_glob_matcher reported errors: {errors:?}");
993        }
994
995        matcher
996    }
997
998    fn make_glob_matcher(glob: &str, implicit: bool) -> NameMatcher {
999        NameMatcher::Glob {
1000            glob: GenericGlob::new(glob.to_owned()).unwrap(),
1001            implicit,
1002        }
1003    }
1004
1005    #[test]
1006    fn test_parse_glob_matcher() {
1007        #[track_caller]
1008        fn assert_glob(input: &str, expected: &str) {
1009            assert_eq!(
1010                make_glob_matcher(expected, false),
1011                parse_glob(input),
1012                "expected matches actual for input {input:?}",
1013            );
1014        }
1015
1016        // Need the closing ) since that's used as the delimiter.
1017        assert_glob(r"#something)", "something");
1018        assert_glob(r"#something*)", "something*");
1019        assert_glob(r"#something?)", "something?");
1020        assert_glob(r"#something[abc])", "something[abc]");
1021        assert_glob(r"#something[!abc])", "something[!abc]");
1022        assert_glob(r"#something[a-c])", "something[a-c]");
1023        assert_glob(r"#foobar\b)", "foobar\u{08}");
1024        assert_glob(r"#foobar\\b)", "foobar\\b");
1025        assert_glob(r"#foobar\))", "foobar)");
1026    }
1027
1028    #[track_caller]
1029    fn parse_set(input: &str) -> ParsedLeaf {
1030        let mut errors = Vec::new();
1031        let span = new_span(input, &mut errors);
1032        parse_set_def.parse_peek(span).unwrap().1.unwrap()
1033    }
1034
1035    macro_rules! assert_parsed_leaf {
1036        ($input: expr, $name:ident, $matches:expr) => {
1037            assert!(matches!($input, ParsedLeaf::$name(x, _) if x == $matches));
1038        };
1039    }
1040
1041    #[test]
1042    fn test_parse_name_matcher() {
1043        // Basic matchers
1044        assert_parsed_leaf!(
1045            parse_set("test(~something)"),
1046            Test,
1047            NameMatcher::Contains {
1048                value: "something".to_string(),
1049                implicit: false,
1050            }
1051        );
1052
1053        assert_parsed_leaf!(
1054            parse_set("test(=something)"),
1055            Test,
1056            NameMatcher::Equal {
1057                value: "something".to_string(),
1058                implicit: false,
1059            }
1060        );
1061        assert_parsed_leaf!(
1062            parse_set("test(/some.*/)"),
1063            Test,
1064            NameMatcher::Regex(regex::Regex::new("some.*").unwrap())
1065        );
1066        assert_parsed_leaf!(
1067            parse_set("test(#something)"),
1068            Test,
1069            make_glob_matcher("something", false)
1070        );
1071        assert_parsed_leaf!(
1072            parse_set("test(#something*)"),
1073            Test,
1074            make_glob_matcher("something*", false)
1075        );
1076        assert_parsed_leaf!(
1077            parse_set(r"test(#something/[?])"),
1078            Test,
1079            make_glob_matcher("something/[?]", false)
1080        );
1081
1082        // Default matchers
1083        assert_parsed_leaf!(
1084            parse_set("test(something)"),
1085            Test,
1086            NameMatcher::Contains {
1087                value: "something".to_string(),
1088                implicit: true,
1089            }
1090        );
1091        assert_parsed_leaf!(
1092            parse_set("package(something)"),
1093            Package,
1094            make_glob_matcher("something", true)
1095        );
1096
1097        // Explicit contains matching
1098        assert_parsed_leaf!(
1099            parse_set("test(~something)"),
1100            Test,
1101            NameMatcher::Contains {
1102                value: "something".to_string(),
1103                implicit: false,
1104            }
1105        );
1106        assert_parsed_leaf!(
1107            parse_set("test(~~something)"),
1108            Test,
1109            NameMatcher::Contains {
1110                value: "~something".to_string(),
1111                implicit: false,
1112            }
1113        );
1114        assert_parsed_leaf!(
1115            parse_set("test(~=something)"),
1116            Test,
1117            NameMatcher::Contains {
1118                value: "=something".to_string(),
1119                implicit: false,
1120            }
1121        );
1122        assert_parsed_leaf!(
1123            parse_set("test(~/something/)"),
1124            Test,
1125            NameMatcher::Contains {
1126                value: "/something/".to_string(),
1127                implicit: false,
1128            }
1129        );
1130        assert_parsed_leaf!(
1131            parse_set("test(~#something)"),
1132            Test,
1133            NameMatcher::Contains {
1134                value: "#something".to_string(),
1135                implicit: false,
1136            }
1137        );
1138
1139        // Explicit equals matching.
1140        assert_parsed_leaf!(
1141            parse_set("test(=something)"),
1142            Test,
1143            NameMatcher::Equal {
1144                value: "something".to_string(),
1145                implicit: false,
1146            }
1147        );
1148        assert_parsed_leaf!(
1149            parse_set("test(=~something)"),
1150            Test,
1151            NameMatcher::Equal {
1152                value: "~something".to_string(),
1153                implicit: false,
1154            }
1155        );
1156        assert_parsed_leaf!(
1157            parse_set("test(==something)"),
1158            Test,
1159            NameMatcher::Equal {
1160                value: "=something".to_string(),
1161                implicit: false,
1162            }
1163        );
1164        assert_parsed_leaf!(
1165            parse_set("test(=/something/)"),
1166            Test,
1167            NameMatcher::Equal {
1168                value: "/something/".to_string(),
1169                implicit: false,
1170            }
1171        );
1172        assert_parsed_leaf!(
1173            parse_set("test(=#something)"),
1174            Test,
1175            NameMatcher::Equal {
1176                value: "#something".to_string(),
1177                implicit: false,
1178            }
1179        );
1180
1181        // Explicit glob matching.
1182        assert_parsed_leaf!(
1183            parse_set("test(#~something)"),
1184            Test,
1185            make_glob_matcher("~something", false)
1186        );
1187        assert_parsed_leaf!(
1188            parse_set("test(#=something)"),
1189            Test,
1190            make_glob_matcher("=something", false)
1191        );
1192        assert_parsed_leaf!(
1193            parse_set("test(#/something/)"),
1194            Test,
1195            make_glob_matcher("/something/", false)
1196        );
1197        assert_parsed_leaf!(
1198            parse_set("test(##something)"),
1199            Test,
1200            make_glob_matcher("#something", false)
1201        );
1202    }
1203
1204    #[test]
1205    fn test_parse_name_matcher_quote() {
1206        assert_parsed_leaf!(
1207            parse_set(r"test(some'thing)"),
1208            Test,
1209            NameMatcher::Contains {
1210                value: r"some'thing".to_string(),
1211                implicit: true,
1212            }
1213        );
1214        assert_parsed_leaf!(
1215            parse_set(r"test(some(thing\))"),
1216            Test,
1217            NameMatcher::Contains {
1218                value: r"some(thing)".to_string(),
1219                implicit: true,
1220            }
1221        );
1222        assert_parsed_leaf!(
1223            parse_set(r"test(some \u{55})"),
1224            Test,
1225            NameMatcher::Contains {
1226                value: r"some U".to_string(),
1227                implicit: true,
1228            }
1229        );
1230    }
1231
1232    #[test]
1233    fn test_parse_set_def() {
1234        assert_eq!(ParsedLeaf::All, parse_set("all()"));
1235        assert_eq!(ParsedLeaf::All, parse_set(" all ( ) "));
1236
1237        assert_eq!(ParsedLeaf::None, parse_set("none()"));
1238
1239        assert_parsed_leaf!(
1240            parse_set("package(=something)"),
1241            Package,
1242            NameMatcher::Equal {
1243                value: "something".to_string(),
1244                implicit: false,
1245            }
1246        );
1247        assert_parsed_leaf!(
1248            parse_set("deps(something)"),
1249            Deps,
1250            make_glob_matcher("something", true)
1251        );
1252        assert_parsed_leaf!(
1253            parse_set("rdeps(something)"),
1254            Rdeps,
1255            make_glob_matcher("something", true)
1256        );
1257        assert_parsed_leaf!(
1258            parse_set("test(something)"),
1259            Test,
1260            NameMatcher::Contains {
1261                value: "something".to_string(),
1262                implicit: true,
1263            }
1264        );
1265        assert_parsed_leaf!(parse_set("platform(host)"), Platform, BuildPlatform::Host);
1266        assert_parsed_leaf!(
1267            parse_set("platform(target)"),
1268            Platform,
1269            BuildPlatform::Target
1270        );
1271        assert_parsed_leaf!(
1272            parse_set("platform(    host    )"),
1273            Platform,
1274            BuildPlatform::Host
1275        );
1276    }
1277
1278    #[track_caller]
1279    fn parse(input: &str) -> ParsedExpr {
1280        match ParsedExpr::parse(input) {
1281            Ok(expr) => expr,
1282            Err(errors) => {
1283                for single_error in &errors {
1284                    let report = miette::Report::new(single_error.clone())
1285                        .with_source_code(input.to_owned());
1286                    eprintln!("{report:?}");
1287                }
1288                panic!("Not a valid expression!")
1289            }
1290        }
1291    }
1292
1293    #[test]
1294    fn test_parse_expr_set() {
1295        let expr = ParsedExpr::all();
1296        assert_eq!(expr, parse("all()"));
1297        assert_eq!(expr, parse("  all ( ) "));
1298        assert_eq!(format!("{expr}"), "all()");
1299    }
1300
1301    #[test]
1302    fn test_parse_expr_not() {
1303        let expr = ParsedExpr::all().not(NotOperator::LiteralNot);
1304        assert_eq_both_ways(&expr, "not all()");
1305        assert_eq!(expr, parse("not  all()"));
1306
1307        let expr = ParsedExpr::all().not(NotOperator::Exclamation);
1308        assert_eq_both_ways(&expr, "! all()");
1309        assert_eq!(expr, parse("!all()"));
1310
1311        let expr = ParsedExpr::all()
1312            .not(NotOperator::LiteralNot)
1313            .not(NotOperator::LiteralNot);
1314        assert_eq_both_ways(&expr, "not not all()");
1315    }
1316
1317    #[test]
1318    fn test_parse_expr_intersection() {
1319        let expr = ParsedExpr::intersection(
1320            AndOperator::LiteralAnd,
1321            ParsedExpr::all(),
1322            ParsedExpr::none(),
1323        );
1324        assert_eq_both_ways(&expr, "all() and none()");
1325        assert_eq!(expr, parse("all()and none()"));
1326
1327        let expr = ParsedExpr::intersection(
1328            AndOperator::Ampersand,
1329            ParsedExpr::all(),
1330            ParsedExpr::none(),
1331        );
1332        assert_eq_both_ways(&expr, "all() & none()");
1333        assert_eq!(expr, parse("all()&none()"));
1334    }
1335
1336    #[test]
1337    fn test_parse_expr_union() {
1338        let expr = ParsedExpr::union(OrOperator::LiteralOr, ParsedExpr::all(), ParsedExpr::none());
1339        assert_eq_both_ways(&expr, "all() or none()");
1340        assert_eq!(expr, parse("all()or none()"));
1341
1342        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none());
1343        assert_eq_both_ways(&expr, "all() | none()");
1344        assert_eq!(expr, parse("all()|none()"));
1345
1346        let expr = ParsedExpr::union(OrOperator::Plus, ParsedExpr::all(), ParsedExpr::none());
1347        assert_eq_both_ways(&expr, "all() + none()");
1348        assert_eq!(expr, parse("all()+none()"));
1349    }
1350
1351    #[test]
1352    fn test_parse_expr_difference() {
1353        let expr = ParsedExpr::difference(
1354            DifferenceOperator::Minus,
1355            ParsedExpr::all(),
1356            ParsedExpr::none(),
1357        );
1358        assert_eq_both_ways(&expr, "all() - none()");
1359        assert_eq!(expr, parse("all()-none()"));
1360    }
1361
1362    #[test]
1363    fn test_parse_expr_precedence() {
1364        let expr = ParsedExpr::intersection(
1365            AndOperator::LiteralAnd,
1366            ParsedExpr::all().not(NotOperator::LiteralNot),
1367            ParsedExpr::none(),
1368        );
1369        assert_eq_both_ways(&expr, "not all() and none()");
1370
1371        let expr = ParsedExpr::intersection(
1372            AndOperator::LiteralAnd,
1373            ParsedExpr::all(),
1374            ParsedExpr::none().not(NotOperator::LiteralNot),
1375        );
1376        assert_eq_both_ways(&expr, "all() and not none()");
1377
1378        let expr = ParsedExpr::intersection(
1379            AndOperator::Ampersand,
1380            ParsedExpr::all(),
1381            ParsedExpr::none(),
1382        );
1383        let expr = ParsedExpr::union(OrOperator::Pipe, expr, ParsedExpr::all());
1384        assert_eq_both_ways(&expr, "all() & none() | all()");
1385
1386        let expr = ParsedExpr::intersection(
1387            AndOperator::Ampersand,
1388            ParsedExpr::none(),
1389            ParsedExpr::all(),
1390        );
1391        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1392        assert_eq_both_ways(&expr, "all() | none() & all()");
1393
1394        let expr =
1395            ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none()).parens();
1396        let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1397        assert_eq_both_ways(&expr, "(all() | none()) & all()");
1398
1399        let expr = ParsedExpr::intersection(
1400            AndOperator::Ampersand,
1401            ParsedExpr::none(),
1402            ParsedExpr::all(),
1403        )
1404        .parens();
1405        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1406        assert_eq_both_ways(&expr, "all() | (none() & all())");
1407
1408        let expr = ParsedExpr::difference(
1409            DifferenceOperator::Minus,
1410            ParsedExpr::all(),
1411            ParsedExpr::none(),
1412        );
1413        let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1414        assert_eq_both_ways(&expr, "all() - none() & all()");
1415
1416        let expr = ParsedExpr::intersection(
1417            AndOperator::Ampersand,
1418            ParsedExpr::all(),
1419            ParsedExpr::none(),
1420        );
1421        let expr = ParsedExpr::difference(DifferenceOperator::Minus, expr, ParsedExpr::all());
1422        assert_eq_both_ways(&expr, "all() & none() - all()");
1423
1424        let expr = ParsedExpr::intersection(
1425            AndOperator::Ampersand,
1426            ParsedExpr::none(),
1427            ParsedExpr::all(),
1428        )
1429        .parens()
1430        .not(NotOperator::LiteralNot);
1431        assert_eq_both_ways(&expr, "not (none() & all())");
1432    }
1433
1434    #[test]
1435    fn test_parse_comma() {
1436        // accept escaped comma
1437        let expr = ParsedExpr::Set(ParsedLeaf::Test(
1438            NameMatcher::Contains {
1439                value: "a,".to_string(),
1440                implicit: false,
1441            },
1442            (5, 4).into(),
1443        ));
1444        assert_eq_both_ways(&expr, r"test(~a\,)");
1445
1446        // string parsing is compatible with possible future syntax
1447        fn parse_future_syntax(
1448            input: &mut Span<'_>,
1449        ) -> PResult<(Option<NameMatcher>, Option<NameMatcher>)> {
1450            let _ = "something".parse_next(input)?;
1451            let _ = '('.parse_next(input)?;
1452            let n1 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1453            let _ = ws(',').parse_next(input)?;
1454            let n2 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1455            let _ = ')'.parse_next(input)?;
1456            Ok((n1, n2))
1457        }
1458
1459        let mut errors = Vec::new();
1460        let mut span = new_span("something(aa, bb)", &mut errors);
1461        if parse_future_syntax.parse_next(&mut span).is_err() {
1462            panic!("Failed to parse comma separated matchers");
1463        }
1464    }
1465
1466    #[track_caller]
1467    fn parse_err(input: &str) -> Vec<ParseSingleError> {
1468        let mut errors = Vec::new();
1469        let span = new_span(input, &mut errors);
1470        super::parse(span).unwrap();
1471        errors
1472    }
1473
1474    macro_rules! assert_error {
1475        ($error:ident, $name:ident, $start:literal, $end:literal) => {{
1476            let matches = matches!($error, ParseSingleError::$name(span) if span == ($start, $end).into());
1477            assert!(
1478                matches,
1479                "expected: {:?}, actual: error: {:?}",
1480                ParseSingleError::$name(($start, $end).into()),
1481                $error,
1482            );
1483        }};
1484    }
1485
1486    #[test]
1487    fn test_invalid_and_operator() {
1488        let src = "all() && none()";
1489        let mut errors = parse_err(src);
1490        assert_eq!(1, errors.len());
1491        let error = errors.remove(0);
1492        assert_error!(error, InvalidAndOperator, 6, 2);
1493
1494        let src = "all() AND none()";
1495        let mut errors = parse_err(src);
1496        assert_eq!(1, errors.len());
1497        let error = errors.remove(0);
1498        assert_error!(error, InvalidAndOperator, 6, 4);
1499    }
1500
1501    #[test]
1502    fn test_invalid_or_operator() {
1503        let src = "all() || none()";
1504        let mut errors = parse_err(src);
1505        assert_eq!(1, errors.len());
1506        let error = errors.remove(0);
1507        assert_error!(error, InvalidOrOperator, 6, 2);
1508
1509        let src = "all() OR none()";
1510        let mut errors = parse_err(src);
1511        assert_eq!(1, errors.len());
1512        let error = errors.remove(0);
1513        assert_error!(error, InvalidOrOperator, 6, 3);
1514    }
1515
1516    #[test]
1517    fn test_missing_close_parentheses() {
1518        let src = "all(";
1519        let mut errors = parse_err(src);
1520        assert_eq!(1, errors.len());
1521        let error = errors.remove(0);
1522        assert_error!(error, ExpectedCloseParenthesis, 4, 0);
1523    }
1524
1525    #[test]
1526    fn test_missing_open_parentheses() {
1527        let src = "all)";
1528        let mut errors = parse_err(src);
1529        assert_eq!(1, errors.len());
1530        let error = errors.remove(0);
1531        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1532    }
1533
1534    #[test]
1535    fn test_missing_parentheses() {
1536        let src = "all";
1537        let mut errors = parse_err(src);
1538        assert_eq!(2, errors.len());
1539        let error = errors.remove(0);
1540        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1541        let error = errors.remove(0);
1542        assert_error!(error, ExpectedCloseParenthesis, 3, 0);
1543    }
1544
1545    #[test]
1546    fn test_invalid_escapes() {
1547        let src = r"package(foobar\$\#\@baz)";
1548        let mut errors = parse_err(src);
1549        assert_eq!(3, errors.len());
1550
1551        // Ensure all three errors are reported.
1552        let error = errors.remove(0);
1553        assert_error!(error, InvalidEscapeCharacter, 14, 2);
1554
1555        let error = errors.remove(0);
1556        assert_error!(error, InvalidEscapeCharacter, 16, 2);
1557
1558        let error = errors.remove(0);
1559        assert_error!(error, InvalidEscapeCharacter, 18, 2);
1560    }
1561
1562    #[test]
1563    fn test_invalid_regex() {
1564        let src = "package(/)/)";
1565        let mut errors = parse_err(src);
1566        assert_eq!(1, errors.len());
1567        let error = errors.remove(0);
1568        assert!(
1569            matches!(error, ParseSingleError::InvalidRegex { span, .. } if span == (9, 1).into())
1570        );
1571
1572        // Ensure more detailed error messages if possible.
1573        let src = "package(/foo(ab/)";
1574        let mut errors = parse_err(src);
1575        assert_eq!(1, errors.len());
1576        let error = errors.remove(0);
1577        let (span, message) = match error {
1578            ParseSingleError::InvalidRegex { span, message } => (span, message),
1579            other => panic!("expected invalid regex with details, found {other}"),
1580        };
1581        assert_eq!(span, (12, 1).into(), "span matches");
1582        assert_eq!(message, "unclosed group");
1583    }
1584
1585    #[test]
1586    fn test_invalid_glob() {
1587        let src = "package(#)";
1588        let mut errors = parse_err(src);
1589        assert_eq!(1, errors.len());
1590        let error = errors.remove(0);
1591        assert_error!(error, InvalidString, 9, 0);
1592
1593        let src = "package(#foo[)";
1594        let mut errors = parse_err(src);
1595        assert_eq!(1, errors.len());
1596        let error = errors.remove(0);
1597        let (span, error) = match error {
1598            ParseSingleError::InvalidGlob { span, error } => (span, error),
1599            other => panic!("expected InvalidGlob with details, found {other}"),
1600        };
1601        assert_eq!(span, (9, 4).into(), "span matches");
1602        assert_eq!(error.to_string(), "unclosed character class; missing ']'");
1603    }
1604
1605    #[test]
1606    fn test_invalid_platform() {
1607        let src = "platform(foo)";
1608        let mut errors = parse_err(src);
1609        assert_eq!(1, errors.len());
1610        let error = errors.remove(0);
1611        assert_error!(error, InvalidPlatformArgument, 9, 3);
1612
1613        let src = "platform(   bar\\t)";
1614        let mut errors = parse_err(src);
1615        assert_eq!(1, errors.len());
1616        let error = errors.remove(0);
1617        assert_error!(error, InvalidPlatformArgument, 9, 8);
1618    }
1619
1620    #[test]
1621    fn test_missing_close_regex() {
1622        let src = "package(/aaa)";
1623        let mut errors = parse_err(src);
1624        assert_eq!(1, errors.len());
1625        let error = errors.remove(0);
1626        assert_error!(error, ExpectedCloseRegex, 12, 0);
1627    }
1628
1629    #[test]
1630    fn test_unexpected_argument() {
1631        let src = "all(aaa)";
1632        let mut errors = parse_err(src);
1633        assert_eq!(1, errors.len());
1634        let error = errors.remove(0);
1635        assert_error!(error, UnexpectedArgument, 4, 3);
1636    }
1637
1638    #[test]
1639    fn test_expected_expr() {
1640        let src = "all() + ";
1641        let mut errors = parse_err(src);
1642        assert_eq!(1, errors.len());
1643        let error = errors.remove(0);
1644        assert_error!(error, ExpectedExpr, 7, 1);
1645    }
1646
1647    #[test]
1648    fn test_expected_eof() {
1649        let src = "all() blabla";
1650        let mut errors = parse_err(src);
1651        assert_eq!(1, errors.len());
1652        let error = errors.remove(0);
1653        assert_error!(error, ExpectedEndOfExpression, 5, 7);
1654    }
1655
1656    #[test]
1657    fn test_missing_argument() {
1658        let src = "test()";
1659        let mut errors = parse_err(src);
1660        assert_eq!(1, errors.len());
1661        let error = errors.remove(0);
1662        assert_error!(error, InvalidString, 5, 0);
1663    }
1664
1665    #[test]
1666    fn test_unexpected_comma() {
1667        let src = "test(aa, )";
1668        let mut errors = parse_err(src);
1669        assert_eq!(1, errors.len());
1670        let error = errors.remove(0);
1671        assert_error!(error, UnexpectedComma, 7, 0);
1672    }
1673
1674    #[test]
1675    fn test_complex_error() {
1676        let src = "all) + package(/not) - deps(expr none)";
1677        let mut errors = parse_err(src);
1678        assert_eq!(2, errors.len(), "{errors:?}");
1679        let error = errors.remove(0);
1680        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1681        let error = errors.remove(0);
1682        assert_error!(error, ExpectedCloseRegex, 19, 0);
1683    }
1684
1685    #[test_strategy::proptest]
1686    fn proptest_expr_roundtrip(#[strategy(ParsedExpr::strategy())] expr: ParsedExpr<()>) {
1687        let expr_string = expr.to_string();
1688        eprintln!("expr string: {expr_string}");
1689        let expr_2 = parse(&expr_string).drop_source_span();
1690
1691        assert_eq!(expr, expr_2, "exprs must roundtrip");
1692    }
1693
1694    #[track_caller]
1695    fn assert_eq_both_ways(expr: &ParsedExpr, string: &str) {
1696        assert_eq!(expr, &parse(string));
1697        assert_eq!(format!("{expr}"), string);
1698    }
1699}