Skip to main content

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