1use 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, not, peek, preceded, repeat, terminated, trace},
24 stream::{AsChar, Location, SliceLen, Stream},
25 token::{literal, one_of, 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#[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 Group(NameMatcher, S),
63 Default(S),
64 All,
65 None,
66}
67
68impl ParsedLeaf {
69 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#[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 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 let effective_start = fragment_start.saturating_add_signed(offset);
287 let effective_end = effective_start + fragment_length;
289 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 word_boundary(input: &mut Span<'_>) -> PResult<()> {
348 peek(not(one_of(|c: char| c.is_alphanum() || c == '_'))).parse_next(input)
349}
350
351fn ws<'a, T, P: ModalParser<Span<'a>, T, Error>>(
352 mut inner: P,
353) -> impl ModalParser<Span<'a>, T, Error> {
354 move |input: &mut Span<'a>| {
355 let start = input.checkpoint();
356 () = repeat(
357 0..,
358 alt((
359 ' '.void(),
361 line_ending.void(),
364 )),
365 )
366 .parse_next(input)?;
367 match inner.parse_next(input) {
368 Ok(res) => Ok(res),
369 Err(winnow::error::ErrMode::Backtrack(err)) => {
370 input.reset(&start);
371 Err(winnow::error::ErrMode::Backtrack(err))
372 }
373 Err(winnow::error::ErrMode::Cut(err)) => {
374 input.reset(&start);
375 Err(winnow::error::ErrMode::Cut(err))
376 }
377 Err(err) => Err(err),
378 }
379 }
380}
381
382fn parse_matcher_text<'i>(input: &mut Span<'i>) -> PResult<Option<String>> {
384 trace("parse_matcher_text", |input: &mut Span<'i>| {
385 let res = match expect(
386 unicode_string::parse_string,
387 ParseSingleError::InvalidString,
388 )
389 .parse_next(input)
390 {
391 Ok(res) => res.flatten(),
392 Err(_) => unreachable!(),
393 };
394
395 if res.as_ref().map(|s| s.is_empty()).unwrap_or(false) {
396 let start = input.current_token_start();
397 input
398 .state
399 .report_error(ParseSingleError::InvalidString((start..0).into()));
400 }
401
402 Ok(res)
403 })
404 .parse_next(input)
405}
406
407fn parse_contains_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
408 trace(
409 "parse_contains_matcher",
410 preceded('~', parse_matcher_text).map(|res: Option<String>| {
411 res.map(|value| NameMatcher::Contains {
412 value,
413 implicit: false,
414 })
415 }),
416 )
417 .parse_next(input)
418}
419
420fn parse_equal_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
421 trace(
422 "parse_equal_matcher",
423 ws(
424 preceded('=', parse_matcher_text).map(|res: Option<String>| {
425 res.map(|value| NameMatcher::Equal {
426 value,
427 implicit: false,
428 })
429 }),
430 ),
431 )
432 .parse_next(input)
433}
434
435fn parse_regex_inner(input: &mut Span<'_>) -> PResult<String> {
436 trace("parse_regex_inner", |input: &mut _| {
437 enum Frag<'a> {
438 Literal(&'a str),
439 Escape(char),
440 }
441
442 let parse_escape = alt((r"\/".value('/'), '\\')).map(Frag::Escape);
443 let parse_literal = take_till(1.., ('\\', '/'))
444 .verify(|s: &str| !s.is_empty())
445 .map(|s: &str| Frag::Literal(s));
446 let parse_frag = alt((parse_escape, parse_literal));
447
448 let res = repeat(0.., parse_frag)
449 .fold(String::new, |mut string, frag| {
450 match frag {
451 Frag::Escape(c) => string.push(c),
452 Frag::Literal(s) => string.push_str(s),
453 }
454 string
455 })
456 .parse_next(input)?;
457
458 let _ = peek('/').parse_next(input)?;
459
460 Ok(res)
461 })
462 .parse_next(input)
463}
464
465pub(crate) struct DisplayParsedRegex<'a>(pub(crate) &'a regex::Regex);
467
468impl fmt::Display for DisplayParsedRegex<'_> {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 let regex = self.0.as_str();
471 let mut escaped = false;
472 for c in regex.chars() {
473 if escaped {
474 escaped = false;
475 write!(f, "{c}")?;
476 } else if c == '\\' {
477 escaped = true;
478 write!(f, "{c}")?;
479 } else if c == '/' {
480 write!(f, "\\/")?;
482 } else {
483 write!(f, "{c}")?;
484 }
485 }
486 Ok(())
487 }
488}
489
490fn parse_regex<'i>(input: &mut Span<'i>) -> PResult<Option<NameMatcher>> {
491 trace("parse_regex", |input: &mut Span<'i>| {
492 let start = input.checkpoint();
493 let res = match parse_regex_inner.parse_next(input) {
494 Ok(res) => res,
495 Err(_) => {
496 input.reset(&start);
497 match take_till::<_, _, Error>(0.., ')').parse_next(input) {
498 Ok(_) => {
499 let start = input.current_token_start();
500 let err = ParseSingleError::ExpectedCloseRegex((start, 0).into());
501 input.state.report_error(err);
502 return Ok(None);
503 }
504 Err(_) => unreachable!(),
505 }
506 }
507 };
508 match regex::Regex::new(&res).map(NameMatcher::Regex) {
509 Ok(res) => Ok(Some(res)),
510 Err(_) => {
511 let end = input.checkpoint();
512
513 input.reset(&start);
514 let start = input.current_token_start();
515
516 input.reset(&end);
517 let end = input.current_token_start();
518
519 let err = ParseSingleError::invalid_regex(&res, start, end);
520 input.state.report_error(err);
521 Ok(None)
522 }
523 }
524 })
525 .parse_next(input)
526}
527
528fn parse_regex_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
529 trace(
530 "parse_regex_matcher",
531 ws(delimited('/', parse_regex, silent_expect(ws('/')))),
532 )
533 .parse_next(input)
534}
535
536fn parse_glob_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
537 trace(
538 "parse_glob_matcher",
539 ws(preceded('#', glob::parse_glob(false))),
540 )
541 .parse_next(input)
542}
543
544fn set_matcher<'a>(
546 default_matcher: DefaultMatcher,
547) -> impl ModalParser<Span<'a>, Option<NameMatcher>, Error> {
548 ws(alt((
549 parse_regex_matcher,
550 parse_glob_matcher,
551 parse_equal_matcher,
552 parse_contains_matcher,
553 default_matcher.into_parser(),
554 )))
555}
556
557fn recover_unexpected_comma<'i>(input: &mut Span<'i>) -> PResult<()> {
558 trace("recover_unexpected_comma", |input: &mut Span<'i>| {
559 let start = input.checkpoint();
560 match peek(ws(',')).parse_next(input) {
561 Ok(_) => {
562 let pos = input.current_token_start();
563 input
564 .state
565 .report_error(ParseSingleError::UnexpectedComma((pos..0).into()));
566 match take_till::<_, _, Error>(0.., ')').parse_next(input) {
567 Ok(_) => Ok(()),
568 Err(_) => unreachable!(),
569 }
570 }
571 Err(_) => {
572 input.reset(&start);
573 Ok(())
574 }
575 }
576 })
577 .parse_next(input)
578}
579
580fn nullary_set_def<'a>(
581 name: &'static str,
582 make_set: fn(SourceSpan) -> ParsedLeaf,
583) -> impl ModalParser<Span<'a>, Option<ParsedLeaf>, Error> {
584 move |i: &mut Span<'_>| {
585 let start = i.current_token_start();
586 let _ = literal(name).parse_next(i)?;
587 let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
588 let err_loc = i.current_token_start();
589 match take_till::<_, _, Error>(0.., ')').parse_next(i) {
590 Ok(res) => {
591 if !res.trim().is_empty() {
592 let span = (err_loc, res.len()).into();
593 let err = ParseSingleError::UnexpectedArgument(span);
594 i.state.report_error(err);
595 }
596 }
597 Err(_) => unreachable!(),
598 };
599 let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
600 let end = i.current_token_start();
601 Ok(Some(make_set((start, end - start).into())))
602 }
603}
604
605#[derive(Copy, Clone, Debug)]
606enum DefaultMatcher {
607 Equal,
609 Contains,
610 Glob,
611}
612
613impl DefaultMatcher {
614 fn into_parser<'a>(self) -> impl ModalParser<Span<'a>, Option<NameMatcher>, Error> {
615 move |input: &mut _| match self {
616 Self::Equal => parse_matcher_text
617 .map(|res: Option<String>| res.map(NameMatcher::implicit_equal))
618 .parse_next(input),
619 Self::Contains => parse_matcher_text
620 .map(|res: Option<String>| res.map(NameMatcher::implicit_contains))
621 .parse_next(input),
622 Self::Glob => glob::parse_glob(true).parse_next(input),
623 }
624 }
625}
626
627fn unary_set_def<'a>(
628 name: &'static str,
629 default_matcher: DefaultMatcher,
630 make_set: fn(NameMatcher, SourceSpan) -> ParsedLeaf,
631) -> impl ModalParser<Span<'a>, Option<ParsedLeaf>, Error> {
632 move |i: &mut _| {
633 let _ = literal(name).parse_next(i)?;
634 let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
635 let start = i.current_token_start();
636 let res = set_matcher(default_matcher).parse_next(i)?;
637 let end = i.current_token_start();
638 recover_unexpected_comma.parse_next(i)?;
639 let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
640 Ok(res.map(|matcher| make_set(matcher, (start, end - start).into())))
641 }
642}
643
644fn platform_def(i: &mut Span<'_>) -> PResult<Option<ParsedLeaf>> {
645 let _ = "platform".parse_next(i)?;
646 let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
647 let start = i.current_token_start();
648 let res = ws(parse_matcher_text).parse_next(i)?;
650 let end = i.current_token_start();
651 recover_unexpected_comma.parse_next(i)?;
652 let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
653
654 let platform = match res.as_deref().map(|res| res.trim()) {
656 Some("host") => Some(BuildPlatform::Host),
657 Some("target") => Some(BuildPlatform::Target),
658 Some(_) => {
659 i.state
660 .report_error(ParseSingleError::InvalidPlatformArgument(
661 (start, end - start).into(),
662 ));
663 None
664 }
665 None => {
666 None
668 }
669 };
670 Ok(platform.map(|platform| ParsedLeaf::Platform(platform, (start, end - start).into())))
671}
672
673fn parse_set_def(input: &mut Span<'_>) -> PResult<Option<ParsedLeaf>> {
674 trace(
675 "parse_set_def",
676 ws(alt((
677 unary_set_def("package", DefaultMatcher::Glob, ParsedLeaf::Package),
678 unary_set_def("deps", DefaultMatcher::Glob, ParsedLeaf::Deps),
679 unary_set_def("rdeps", DefaultMatcher::Glob, ParsedLeaf::Rdeps),
680 unary_set_def("kind", DefaultMatcher::Equal, ParsedLeaf::Kind),
681 unary_set_def("binary_id", DefaultMatcher::Glob, ParsedLeaf::BinaryId),
683 unary_set_def("binary", DefaultMatcher::Glob, ParsedLeaf::Binary),
684 unary_set_def("test", DefaultMatcher::Contains, ParsedLeaf::Test),
685 alt((
686 unary_set_def("group", DefaultMatcher::Glob, ParsedLeaf::Group),
687 platform_def,
688 nullary_set_def("default", ParsedLeaf::Default),
689 nullary_set_def("all", |_| ParsedLeaf::All),
690 nullary_set_def("none", |_| ParsedLeaf::None),
691 )),
692 ))),
693 )
694 .parse_next(input)
695}
696
697fn expect_expr<'a, P: ModalParser<Span<'a>, ExprResult, Error>>(
698 inner: P,
699) -> impl ModalParser<Span<'a>, ExprResult, Error> {
700 expect(inner, ParseSingleError::ExpectedExpr).map(|res| res.unwrap_or(ExprResult::Error))
701}
702
703fn parse_parentheses_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
704 trace(
705 "parse_parentheses_expr",
706 delimited(
707 '(',
708 expect_expr(parse_expr),
709 expect_char(')', ParseSingleError::ExpectedCloseParenthesis),
710 )
711 .map(|expr| expr.parens()),
712 )
713 .parse_next(input)
714}
715
716fn parse_basic_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
717 trace(
718 "parse_basic_expr",
719 ws(alt((
720 parse_set_def.map(|set| {
721 set.map(|set| ExprResult::Valid(ParsedExpr::Set(set)))
722 .unwrap_or(ExprResult::Error)
723 }),
724 parse_expr_not,
725 parse_parentheses_expr,
726 parse_misplaced_binary_op,
732 parse_misplaced_unary_op,
733 ))),
734 )
735 .parse_next(input)
736}
737
738fn parse_misplaced_binary_op(input: &mut Span<'_>) -> PResult<ExprResult> {
739 trace("parse_misplaced_binary_op", |input: &mut Span<'_>| {
740 let start = input.current_token_start();
741 let (op, suggest) = alt((
742 terminated("and", word_boundary).value(("and", "and")),
743 terminated("or", word_boundary).value(("or", "or")),
744 terminated("AND", word_boundary).value(("AND", "and")),
745 terminated("OR", word_boundary).value(("OR", "or")),
746 "&&".value(("&&", "&")),
747 "||".value(("||", "|")),
748 ))
749 .parse_next(input)?;
750 let err = ParseSingleError::ExprFoundBinaryOp {
751 op,
752 suggest,
753 span: (start, op.len()).into(),
754 };
755 input.state.report_error(err);
756 Ok(ExprResult::Error)
757 })
758 .parse_next(input)
759}
760
761fn parse_misplaced_unary_op(input: &mut Span<'_>) -> PResult<ExprResult> {
762 trace("parse_misplaced_unary_op", |input: &mut Span<'_>| {
763 let start = input.current_token_start();
764 let op: &'static str = terminated("NOT", word_boundary)
765 .value("NOT")
766 .parse_next(input)?;
767 let err = ParseSingleError::ExprFoundUnaryOp {
768 op,
769 suggest: "not",
770 span: (start, op.len()).into(),
771 };
772 input.state.report_error(err);
773 Ok(ExprResult::Error)
774 })
775 .parse_next(input)
776}
777
778#[derive(Clone, Copy, Debug, Eq, PartialEq)]
779#[cfg_attr(
780 any(test, feature = "internal-testing"),
781 derive(test_strategy::Arbitrary)
782)]
783pub enum NotOperator {
784 LiteralNot,
785 Exclamation,
786}
787
788impl fmt::Display for NotOperator {
789 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790 match self {
791 NotOperator::LiteralNot => f.write_str("not"),
792 NotOperator::Exclamation => f.write_str("!"),
793 }
794 }
795}
796
797fn parse_expr_not(input: &mut Span<'_>) -> PResult<ExprResult> {
798 trace(
799 "parse_expr_not",
800 (
801 alt((
802 terminated("not", word_boundary).value(NotOperator::LiteralNot),
803 '!'.value(NotOperator::Exclamation),
804 )),
805 expect_expr(ws(parse_basic_expr)),
806 )
807 .map(|(op, expr)| expr.negate(op)),
808 )
809 .parse_next(input)
810}
811
812#[derive(Clone, Copy, Debug, Eq, PartialEq)]
815#[cfg_attr(
816 any(test, feature = "internal-testing"),
817 derive(test_strategy::Arbitrary)
818)]
819pub enum OrOperator {
820 LiteralOr,
821 Pipe,
822 Plus,
823}
824
825impl fmt::Display for OrOperator {
826 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
827 match self {
828 OrOperator::LiteralOr => f.write_str("or"),
829 OrOperator::Pipe => f.write_str("|"),
830 OrOperator::Plus => f.write_str("+"),
831 }
832 }
833}
834
835fn parse_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
836 trace("parse_expr", |input: &mut _| {
837 let expr = expect_expr(parse_and_or_difference_expr).parse_next(input)?;
839
840 let ops = repeat(
841 0..,
842 (parse_or_operator, expect_expr(parse_and_or_difference_expr)),
843 )
844 .fold(Vec::new, |mut ops, (op, expr)| {
845 ops.push((op, expr));
846 ops
847 })
848 .parse_next(input)?;
849
850 let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| {
851 if let Some(op) = op {
852 expr_1.combine(
853 |expr_1, expr_2| ParsedExpr::union(op, expr_1, expr_2),
854 expr_2,
855 )
856 } else {
857 ExprResult::Error
858 }
859 });
860
861 Ok(expr)
862 })
863 .parse_next(input)
864}
865
866fn parse_or_operator<'i>(input: &mut Span<'i>) -> PResult<Option<OrOperator>> {
867 trace(
868 "parse_or_operator",
869 ws(alt((
870 |input: &mut Span<'i>| {
871 let start = input.current_token_start();
872 let op = alt(("||", terminated("OR", word_boundary))).parse_next(input)?;
875 let length = op.len();
877 let err = ParseSingleError::InvalidOrOperator((start, length).into());
878 input.state.report_error(err);
879 Ok(None)
880 },
881 terminated("or", word_boundary).value(Some(OrOperator::LiteralOr)),
882 '|'.value(Some(OrOperator::Pipe)),
883 '+'.value(Some(OrOperator::Plus)),
884 ))),
885 )
886 .parse_next(input)
887}
888
889#[derive(Clone, Copy, Debug, Eq, PartialEq)]
892#[cfg_attr(
893 any(test, feature = "internal-testing"),
894 derive(test_strategy::Arbitrary)
895)]
896pub enum AndOperator {
897 LiteralAnd,
898 Ampersand,
899}
900
901impl fmt::Display for AndOperator {
902 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
903 match self {
904 AndOperator::LiteralAnd => f.write_str("and"),
905 AndOperator::Ampersand => f.write_str("&"),
906 }
907 }
908}
909
910#[derive(Clone, Copy, Debug, Eq, PartialEq)]
911#[cfg_attr(
912 any(test, feature = "internal-testing"),
913 derive(test_strategy::Arbitrary)
914)]
915pub enum DifferenceOperator {
916 Minus,
917}
918
919impl fmt::Display for DifferenceOperator {
920 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
921 match self {
922 DifferenceOperator::Minus => f.write_str("-"),
923 }
924 }
925}
926
927#[derive(Clone, Copy, Debug, Eq, PartialEq)]
928enum AndOrDifferenceOperator {
929 And(AndOperator),
930 Difference(DifferenceOperator),
931}
932
933fn parse_and_or_difference_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
934 trace("parse_and_or_difference_expr", |input: &mut _| {
935 let expr = expect_expr(parse_basic_expr).parse_next(input)?;
936
937 let ops = repeat(
938 0..,
939 (
940 parse_and_or_difference_operator,
941 expect_expr(parse_basic_expr),
942 ),
943 )
944 .fold(Vec::new, |mut ops, (op, expr)| {
945 ops.push((op, expr));
946 ops
947 })
948 .parse_next(input)?;
949
950 let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| match op {
951 Some(AndOrDifferenceOperator::And(op)) => expr_1.combine(
952 |expr_1, expr_2| ParsedExpr::intersection(op, expr_1, expr_2),
953 expr_2,
954 ),
955 Some(AndOrDifferenceOperator::Difference(op)) => expr_1.combine(
956 |expr_1, expr_2| ParsedExpr::difference(op, expr_1, expr_2),
957 expr_2,
958 ),
959 None => ExprResult::Error,
960 });
961
962 Ok(expr)
963 })
964 .parse_next(input)
965}
966
967fn parse_and_or_difference_operator<'i>(
968 input: &mut Span<'i>,
969) -> PResult<Option<AndOrDifferenceOperator>> {
970 trace(
971 "parse_and_or_difference_operator",
972 ws(alt((
973 |input: &mut Span<'i>| {
974 let start = input.current_token_start();
975 let op = alt(("&&", terminated("AND", word_boundary))).parse_next(input)?;
976 let length = op.len();
978 let err = ParseSingleError::InvalidAndOperator((start, length).into());
979 input.state.report_error(err);
980 Ok(None)
981 },
982 terminated("and", word_boundary)
983 .value(Some(AndOrDifferenceOperator::And(AndOperator::LiteralAnd))),
984 '&'.value(Some(AndOrDifferenceOperator::And(AndOperator::Ampersand))),
985 '-'.value(Some(AndOrDifferenceOperator::Difference(
986 DifferenceOperator::Minus,
987 ))),
988 ))),
989 )
990 .parse_next(input)
991}
992
993pub(crate) fn parse(input: Span<'_>) -> Result<ExprResult, winnow::error::ErrMode<Error>> {
996 let (_, expr) = terminated(
997 parse_expr,
998 expect(ws(eof), ParseSingleError::ExpectedEndOfExpression),
999 )
1000 .parse_peek(input)?;
1001 Ok(expr)
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006 use super::*;
1007
1008 #[track_caller]
1009 fn parse_regex(input: &str) -> NameMatcher {
1010 let mut errors = Vec::new();
1011 let span = new_span(input, &mut errors);
1012 parse_regex_matcher.parse_peek(span).unwrap().1.unwrap()
1013 }
1014
1015 #[test]
1016 fn test_parse_regex() {
1017 assert_eq!(
1018 NameMatcher::Regex(regex::Regex::new(r"some.*").unwrap()),
1019 parse_regex(r"/some.*/")
1020 );
1021
1022 assert_eq!(
1023 NameMatcher::Regex(regex::Regex::new(r"a/a").unwrap()),
1024 parse_regex(r"/a\/a/")
1025 );
1026
1027 assert_eq!(
1028 NameMatcher::Regex(regex::Regex::new(r"\w/a").unwrap()),
1029 parse_regex(r"/\w\/a/")
1030 );
1031
1032 assert_eq!(
1033 NameMatcher::Regex(regex::Regex::new(r"\w\\/a").unwrap()),
1034 parse_regex(r"/\w\\\/a/")
1035 );
1036
1037 assert_eq!(
1038 NameMatcher::Regex(regex::Regex::new(r"\p{Greek}\\/a").unwrap()),
1039 parse_regex(r"/\p{Greek}\\\/a/")
1040 );
1041 }
1042
1043 #[track_caller]
1044 fn parse_glob(input: &str) -> NameMatcher {
1045 let mut errors = Vec::new();
1046 let span = new_span(input, &mut errors);
1047 let matcher = parse_glob_matcher
1048 .parse_peek(span)
1049 .unwrap_or_else(|error| {
1050 panic!("for input {input}, parse_glob_matcher returned an error: {error}")
1051 })
1052 .1
1053 .unwrap_or_else(|| {
1054 panic!(
1055 "for input {input}, parse_glob_matcher returned None \
1056 (reported errors: {errors:?})"
1057 )
1058 });
1059 if !errors.is_empty() {
1060 panic!("for input {input}, parse_glob_matcher reported errors: {errors:?}");
1061 }
1062
1063 matcher
1064 }
1065
1066 fn make_glob_matcher(glob: &str, implicit: bool) -> NameMatcher {
1067 NameMatcher::Glob {
1068 glob: GenericGlob::new(glob.to_owned()).unwrap(),
1069 implicit,
1070 }
1071 }
1072
1073 #[test]
1074 fn test_parse_glob_matcher() {
1075 #[track_caller]
1076 fn assert_glob(input: &str, expected: &str) {
1077 assert_eq!(
1078 make_glob_matcher(expected, false),
1079 parse_glob(input),
1080 "expected matches actual for input {input:?}",
1081 );
1082 }
1083
1084 assert_glob(r"#something)", "something");
1086 assert_glob(r"#something*)", "something*");
1087 assert_glob(r"#something?)", "something?");
1088 assert_glob(r"#something[abc])", "something[abc]");
1089 assert_glob(r"#something[!abc])", "something[!abc]");
1090 assert_glob(r"#something[a-c])", "something[a-c]");
1091 assert_glob(r"#foobar\b)", "foobar\u{08}");
1092 assert_glob(r"#foobar\\b)", "foobar\\b");
1093 assert_glob(r"#foobar\))", "foobar)");
1094 }
1095
1096 #[track_caller]
1097 fn parse_set(input: &str) -> ParsedLeaf {
1098 let mut errors = Vec::new();
1099 let span = new_span(input, &mut errors);
1100 parse_set_def.parse_peek(span).unwrap().1.unwrap()
1101 }
1102
1103 macro_rules! assert_parsed_leaf {
1104 ($input: expr, $name:ident, $matches:expr) => {
1105 assert!(matches!($input, ParsedLeaf::$name(x, _) if x == $matches));
1106 };
1107 }
1108
1109 #[test]
1110 fn test_parse_name_matcher() {
1111 assert_parsed_leaf!(
1113 parse_set("test(~something)"),
1114 Test,
1115 NameMatcher::Contains {
1116 value: "something".to_string(),
1117 implicit: false,
1118 }
1119 );
1120
1121 assert_parsed_leaf!(
1122 parse_set("test(=something)"),
1123 Test,
1124 NameMatcher::Equal {
1125 value: "something".to_string(),
1126 implicit: false,
1127 }
1128 );
1129 assert_parsed_leaf!(
1130 parse_set("test(/some.*/)"),
1131 Test,
1132 NameMatcher::Regex(regex::Regex::new("some.*").unwrap())
1133 );
1134 assert_parsed_leaf!(
1135 parse_set("test(#something)"),
1136 Test,
1137 make_glob_matcher("something", false)
1138 );
1139 assert_parsed_leaf!(
1140 parse_set("test(#something*)"),
1141 Test,
1142 make_glob_matcher("something*", false)
1143 );
1144 assert_parsed_leaf!(
1145 parse_set(r"test(#something/[?])"),
1146 Test,
1147 make_glob_matcher("something/[?]", false)
1148 );
1149
1150 assert_parsed_leaf!(
1152 parse_set("test(something)"),
1153 Test,
1154 NameMatcher::Contains {
1155 value: "something".to_string(),
1156 implicit: true,
1157 }
1158 );
1159 assert_parsed_leaf!(
1160 parse_set("package(something)"),
1161 Package,
1162 make_glob_matcher("something", true)
1163 );
1164
1165 assert_parsed_leaf!(
1167 parse_set("test(~something)"),
1168 Test,
1169 NameMatcher::Contains {
1170 value: "something".to_string(),
1171 implicit: false,
1172 }
1173 );
1174 assert_parsed_leaf!(
1175 parse_set("test(~~something)"),
1176 Test,
1177 NameMatcher::Contains {
1178 value: "~something".to_string(),
1179 implicit: false,
1180 }
1181 );
1182 assert_parsed_leaf!(
1183 parse_set("test(~=something)"),
1184 Test,
1185 NameMatcher::Contains {
1186 value: "=something".to_string(),
1187 implicit: false,
1188 }
1189 );
1190 assert_parsed_leaf!(
1191 parse_set("test(~/something/)"),
1192 Test,
1193 NameMatcher::Contains {
1194 value: "/something/".to_string(),
1195 implicit: false,
1196 }
1197 );
1198 assert_parsed_leaf!(
1199 parse_set("test(~#something)"),
1200 Test,
1201 NameMatcher::Contains {
1202 value: "#something".to_string(),
1203 implicit: false,
1204 }
1205 );
1206
1207 assert_parsed_leaf!(
1209 parse_set("test(=something)"),
1210 Test,
1211 NameMatcher::Equal {
1212 value: "something".to_string(),
1213 implicit: false,
1214 }
1215 );
1216 assert_parsed_leaf!(
1217 parse_set("test(=~something)"),
1218 Test,
1219 NameMatcher::Equal {
1220 value: "~something".to_string(),
1221 implicit: false,
1222 }
1223 );
1224 assert_parsed_leaf!(
1225 parse_set("test(==something)"),
1226 Test,
1227 NameMatcher::Equal {
1228 value: "=something".to_string(),
1229 implicit: false,
1230 }
1231 );
1232 assert_parsed_leaf!(
1233 parse_set("test(=/something/)"),
1234 Test,
1235 NameMatcher::Equal {
1236 value: "/something/".to_string(),
1237 implicit: false,
1238 }
1239 );
1240 assert_parsed_leaf!(
1241 parse_set("test(=#something)"),
1242 Test,
1243 NameMatcher::Equal {
1244 value: "#something".to_string(),
1245 implicit: false,
1246 }
1247 );
1248
1249 assert_parsed_leaf!(
1251 parse_set("test(#~something)"),
1252 Test,
1253 make_glob_matcher("~something", false)
1254 );
1255 assert_parsed_leaf!(
1256 parse_set("test(#=something)"),
1257 Test,
1258 make_glob_matcher("=something", false)
1259 );
1260 assert_parsed_leaf!(
1261 parse_set("test(#/something/)"),
1262 Test,
1263 make_glob_matcher("/something/", false)
1264 );
1265 assert_parsed_leaf!(
1266 parse_set("test(##something)"),
1267 Test,
1268 make_glob_matcher("#something", false)
1269 );
1270 }
1271
1272 #[test]
1273 fn test_parse_name_matcher_quote() {
1274 assert_parsed_leaf!(
1275 parse_set(r"test(some'thing)"),
1276 Test,
1277 NameMatcher::Contains {
1278 value: r"some'thing".to_string(),
1279 implicit: true,
1280 }
1281 );
1282 assert_parsed_leaf!(
1283 parse_set(r"test(some(thing\))"),
1284 Test,
1285 NameMatcher::Contains {
1286 value: r"some(thing)".to_string(),
1287 implicit: true,
1288 }
1289 );
1290 assert_parsed_leaf!(
1291 parse_set(r"test(some \u{55})"),
1292 Test,
1293 NameMatcher::Contains {
1294 value: r"some U".to_string(),
1295 implicit: true,
1296 }
1297 );
1298 }
1299
1300 #[test]
1301 fn test_parse_set_def() {
1302 assert_eq!(ParsedLeaf::All, parse_set("all()"));
1303 assert_eq!(ParsedLeaf::All, parse_set(" all ( ) "));
1304
1305 assert_eq!(ParsedLeaf::None, parse_set("none()"));
1306
1307 assert_parsed_leaf!(
1308 parse_set("package(=something)"),
1309 Package,
1310 NameMatcher::Equal {
1311 value: "something".to_string(),
1312 implicit: false,
1313 }
1314 );
1315 assert_parsed_leaf!(
1316 parse_set("deps(something)"),
1317 Deps,
1318 make_glob_matcher("something", true)
1319 );
1320 assert_parsed_leaf!(
1321 parse_set("rdeps(something)"),
1322 Rdeps,
1323 make_glob_matcher("something", true)
1324 );
1325 assert_parsed_leaf!(
1326 parse_set("test(something)"),
1327 Test,
1328 NameMatcher::Contains {
1329 value: "something".to_string(),
1330 implicit: true,
1331 }
1332 );
1333 assert_parsed_leaf!(parse_set("platform(host)"), Platform, BuildPlatform::Host);
1334 assert_parsed_leaf!(
1335 parse_set("platform(target)"),
1336 Platform,
1337 BuildPlatform::Target
1338 );
1339 assert_parsed_leaf!(
1340 parse_set("platform( host )"),
1341 Platform,
1342 BuildPlatform::Host
1343 );
1344 }
1345
1346 #[track_caller]
1347 fn parse(input: &str) -> ParsedExpr {
1348 match ParsedExpr::parse(input) {
1349 Ok(expr) => expr,
1350 Err(errors) => {
1351 for single_error in &errors {
1352 let report = miette::Report::new(single_error.clone())
1353 .with_source_code(input.to_owned());
1354 eprintln!("{report:?}");
1355 }
1356 panic!("Not a valid expression!")
1357 }
1358 }
1359 }
1360
1361 #[test]
1362 fn reference_documents_all_predicates() {
1363 fn predicate_name(leaf: &ParsedLeaf) -> &'static str {
1364 match leaf {
1367 ParsedLeaf::All => "all",
1368 ParsedLeaf::None => "none",
1369 ParsedLeaf::Test(..) => "test",
1370 ParsedLeaf::Group(..) => "group",
1371 ParsedLeaf::Package(..) => "package",
1372 ParsedLeaf::Deps(..) => "deps",
1373 ParsedLeaf::Rdeps(..) => "rdeps",
1374 ParsedLeaf::BinaryId(..) => "binary_id",
1375 ParsedLeaf::Kind(..) => "kind",
1376 ParsedLeaf::Binary(..) => "binary",
1377 ParsedLeaf::Platform(..) => "platform",
1378 ParsedLeaf::Default(..) => "default",
1379 }
1380 }
1381
1382 let samples = [
1385 "all()",
1386 "none()",
1387 "test(x)",
1388 "group(x)",
1389 "package(x)",
1390 "deps(x)",
1391 "rdeps(x)",
1392 "binary_id(x)",
1393 "kind(lib)",
1394 "binary(x)",
1395 "platform(host)",
1396 "default()",
1397 ];
1398
1399 let mut variants = std::collections::HashSet::new();
1400 for sample in samples {
1401 let ParsedExpr::Set(leaf) = parse(sample) else {
1402 panic!("`{sample}` should parse to a single set predicate");
1403 };
1404 variants.insert(std::mem::discriminant(&leaf));
1405
1406 let name = predicate_name(&leaf);
1407 assert!(
1408 crate::FILTERSET_REFERENCE_MD.contains(&format!("`{name}(")),
1409 "predicate `{name}()` is accepted by the parser but not documented in the \
1410 filterset reference (nextest-filtering/reference.md)",
1411 );
1412 }
1413
1414 assert_eq!(
1415 variants.len(),
1416 samples.len(),
1417 "samples should each use a distinct predicate",
1418 );
1419 }
1420
1421 #[test]
1422 fn test_parse_expr_set() {
1423 let expr = ParsedExpr::all();
1424 assert_eq!(expr, parse("all()"));
1425 assert_eq!(expr, parse(" all ( ) "));
1426 assert_eq!(format!("{expr}"), "all()");
1427 }
1428
1429 #[test]
1430 fn test_parse_expr_not() {
1431 let expr = ParsedExpr::all().not(NotOperator::LiteralNot);
1432 assert_eq_both_ways(&expr, "not all()");
1433 assert_eq!(expr, parse("not all()"));
1434
1435 let expr = ParsedExpr::all().not(NotOperator::Exclamation);
1436 assert_eq_both_ways(&expr, "! all()");
1437 assert_eq!(expr, parse("!all()"));
1438
1439 let expr = ParsedExpr::all()
1440 .not(NotOperator::LiteralNot)
1441 .not(NotOperator::LiteralNot);
1442 assert_eq_both_ways(&expr, "not not all()");
1443
1444 let expr = ParsedExpr::all().parens().not(NotOperator::LiteralNot);
1447 assert_eq!(expr, parse("not(all())"));
1448 assert_eq_both_ways(&expr, "not (all())");
1449
1450 let expr = ParsedExpr::all()
1452 .not(NotOperator::Exclamation)
1453 .not(NotOperator::LiteralNot);
1454 assert_eq!(expr, parse("not!all()"));
1455 }
1456
1457 #[test]
1458 fn test_parse_expr_intersection() {
1459 let expr = ParsedExpr::intersection(
1460 AndOperator::LiteralAnd,
1461 ParsedExpr::all(),
1462 ParsedExpr::none(),
1463 );
1464 assert_eq_both_ways(&expr, "all() and none()");
1465 assert_eq!(expr, parse("all()and none()"));
1466
1467 let expr = ParsedExpr::intersection(
1468 AndOperator::Ampersand,
1469 ParsedExpr::all(),
1470 ParsedExpr::none(),
1471 );
1472 assert_eq_both_ways(&expr, "all() & none()");
1473 assert_eq!(expr, parse("all()&none()"));
1474
1475 let expr = ParsedExpr::intersection(
1477 AndOperator::LiteralAnd,
1478 ParsedExpr::all(),
1479 ParsedExpr::none().parens(),
1480 );
1481 assert_eq!(expr, parse("all()and(none())"));
1482 assert_eq!(expr, parse("all() and(none())"));
1483 }
1484
1485 #[test]
1486 fn test_parse_expr_union() {
1487 let expr = ParsedExpr::union(OrOperator::LiteralOr, ParsedExpr::all(), ParsedExpr::none());
1488 assert_eq_both_ways(&expr, "all() or none()");
1489 assert_eq!(expr, parse("all()or none()"));
1490
1491 let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none());
1492 assert_eq_both_ways(&expr, "all() | none()");
1493 assert_eq!(expr, parse("all()|none()"));
1494
1495 let expr = ParsedExpr::union(OrOperator::Plus, ParsedExpr::all(), ParsedExpr::none());
1496 assert_eq_both_ways(&expr, "all() + none()");
1497 assert_eq!(expr, parse("all()+none()"));
1498
1499 let expr = ParsedExpr::union(
1501 OrOperator::LiteralOr,
1502 ParsedExpr::all(),
1503 ParsedExpr::none().parens(),
1504 );
1505 assert_eq!(expr, parse("all()or(none())"));
1506 assert_eq!(expr, parse("all() or(none())"));
1507 }
1508
1509 #[test]
1510 fn test_parse_expr_difference() {
1511 let expr = ParsedExpr::difference(
1512 DifferenceOperator::Minus,
1513 ParsedExpr::all(),
1514 ParsedExpr::none(),
1515 );
1516 assert_eq_both_ways(&expr, "all() - none()");
1517 assert_eq!(expr, parse("all()-none()"));
1518 }
1519
1520 #[test]
1521 fn test_parse_expr_precedence() {
1522 let expr = ParsedExpr::intersection(
1523 AndOperator::LiteralAnd,
1524 ParsedExpr::all().not(NotOperator::LiteralNot),
1525 ParsedExpr::none(),
1526 );
1527 assert_eq_both_ways(&expr, "not all() and none()");
1528
1529 let expr = ParsedExpr::intersection(
1530 AndOperator::LiteralAnd,
1531 ParsedExpr::all(),
1532 ParsedExpr::none().not(NotOperator::LiteralNot),
1533 );
1534 assert_eq_both_ways(&expr, "all() and not none()");
1535
1536 let expr = ParsedExpr::intersection(
1537 AndOperator::Ampersand,
1538 ParsedExpr::all(),
1539 ParsedExpr::none(),
1540 );
1541 let expr = ParsedExpr::union(OrOperator::Pipe, expr, ParsedExpr::all());
1542 assert_eq_both_ways(&expr, "all() & none() | all()");
1543
1544 let expr = ParsedExpr::intersection(
1545 AndOperator::Ampersand,
1546 ParsedExpr::none(),
1547 ParsedExpr::all(),
1548 );
1549 let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1550 assert_eq_both_ways(&expr, "all() | none() & all()");
1551
1552 let expr =
1553 ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none()).parens();
1554 let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1555 assert_eq_both_ways(&expr, "(all() | none()) & all()");
1556
1557 let expr = ParsedExpr::intersection(
1558 AndOperator::Ampersand,
1559 ParsedExpr::none(),
1560 ParsedExpr::all(),
1561 )
1562 .parens();
1563 let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1564 assert_eq_both_ways(&expr, "all() | (none() & all())");
1565
1566 let expr = ParsedExpr::difference(
1567 DifferenceOperator::Minus,
1568 ParsedExpr::all(),
1569 ParsedExpr::none(),
1570 );
1571 let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1572 assert_eq_both_ways(&expr, "all() - none() & all()");
1573
1574 let expr = ParsedExpr::intersection(
1575 AndOperator::Ampersand,
1576 ParsedExpr::all(),
1577 ParsedExpr::none(),
1578 );
1579 let expr = ParsedExpr::difference(DifferenceOperator::Minus, expr, ParsedExpr::all());
1580 assert_eq_both_ways(&expr, "all() & none() - all()");
1581
1582 let expr = ParsedExpr::intersection(
1583 AndOperator::Ampersand,
1584 ParsedExpr::none(),
1585 ParsedExpr::all(),
1586 )
1587 .parens()
1588 .not(NotOperator::LiteralNot);
1589 assert_eq_both_ways(&expr, "not (none() & all())");
1590 }
1591
1592 #[test]
1593 fn test_parse_comma() {
1594 let expr = ParsedExpr::Set(ParsedLeaf::Test(
1596 NameMatcher::Contains {
1597 value: "a,".to_string(),
1598 implicit: false,
1599 },
1600 (5, 4).into(),
1601 ));
1602 assert_eq_both_ways(&expr, r"test(~a\,)");
1603
1604 fn parse_future_syntax(
1606 input: &mut Span<'_>,
1607 ) -> PResult<(Option<NameMatcher>, Option<NameMatcher>)> {
1608 let _ = "something".parse_next(input)?;
1609 let _ = '('.parse_next(input)?;
1610 let n1 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1611 let _ = ws(',').parse_next(input)?;
1612 let n2 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1613 let _ = ')'.parse_next(input)?;
1614 Ok((n1, n2))
1615 }
1616
1617 let mut errors = Vec::new();
1618 let mut span = new_span("something(aa, bb)", &mut errors);
1619 if parse_future_syntax.parse_next(&mut span).is_err() {
1620 panic!("Failed to parse comma separated matchers");
1621 }
1622 }
1623
1624 #[track_caller]
1625 fn parse_err(input: &str) -> Vec<ParseSingleError> {
1626 let mut errors = Vec::new();
1627 let span = new_span(input, &mut errors);
1628 super::parse(span).unwrap();
1629 errors
1630 }
1631
1632 macro_rules! assert_error {
1633 ($error:ident, $name:ident, $start:literal, $end:literal) => {{
1634 let matches = matches!($error, ParseSingleError::$name(span) if span == ($start, $end).into());
1635 assert!(
1636 matches,
1637 "expected: {:?}, actual: error: {:?}",
1638 ParseSingleError::$name(($start, $end).into()),
1639 $error,
1640 );
1641 }};
1642 }
1643
1644 #[test]
1645 fn test_invalid_and_operator() {
1646 let src = "all() && none()";
1647 let mut errors = parse_err(src);
1648 assert_eq!(1, errors.len());
1649 let error = errors.remove(0);
1650 assert_error!(error, InvalidAndOperator, 6, 2);
1651
1652 let src = "all() AND none()";
1653 let mut errors = parse_err(src);
1654 assert_eq!(1, errors.len());
1655 let error = errors.remove(0);
1656 assert_error!(error, InvalidAndOperator, 6, 3);
1657
1658 let src = "all() AND(none())";
1661 let mut errors = parse_err(src);
1662 assert_eq!(1, errors.len());
1663 let error = errors.remove(0);
1664 assert_error!(error, InvalidAndOperator, 6, 3);
1665 }
1666
1667 #[test]
1668 fn test_invalid_or_operator() {
1669 let src = "all() || none()";
1670 let mut errors = parse_err(src);
1671 assert_eq!(1, errors.len());
1672 let error = errors.remove(0);
1673 assert_error!(error, InvalidOrOperator, 6, 2);
1674
1675 let src = "all() OR none()";
1676 let mut errors = parse_err(src);
1677 assert_eq!(1, errors.len());
1678 let error = errors.remove(0);
1679 assert_error!(error, InvalidOrOperator, 6, 2);
1680
1681 let src = "all() OR(none())";
1682 let mut errors = parse_err(src);
1683 assert_eq!(1, errors.len());
1684 let error = errors.remove(0);
1685 assert_error!(error, InvalidOrOperator, 6, 2);
1686 }
1687
1688 #[test]
1689 fn test_missing_close_parentheses() {
1690 let src = "all(";
1691 let mut errors = parse_err(src);
1692 assert_eq!(1, errors.len());
1693 let error = errors.remove(0);
1694 assert_error!(error, ExpectedCloseParenthesis, 4, 0);
1695 }
1696
1697 #[test]
1698 fn test_missing_open_parentheses() {
1699 let src = "all)";
1700 let mut errors = parse_err(src);
1701 assert_eq!(1, errors.len());
1702 let error = errors.remove(0);
1703 assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1704 }
1705
1706 #[test]
1707 fn test_missing_parentheses() {
1708 let src = "all";
1709 let mut errors = parse_err(src);
1710 assert_eq!(2, errors.len());
1711 let error = errors.remove(0);
1712 assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1713 let error = errors.remove(0);
1714 assert_error!(error, ExpectedCloseParenthesis, 3, 0);
1715 }
1716
1717 #[test]
1718 fn test_invalid_escapes() {
1719 let src = r"package(foobar\$\#\@baz)";
1720 let mut errors = parse_err(src);
1721 assert_eq!(3, errors.len());
1722
1723 let error = errors.remove(0);
1725 assert_error!(error, InvalidEscapeCharacter, 14, 2);
1726
1727 let error = errors.remove(0);
1728 assert_error!(error, InvalidEscapeCharacter, 16, 2);
1729
1730 let error = errors.remove(0);
1731 assert_error!(error, InvalidEscapeCharacter, 18, 2);
1732 }
1733
1734 #[test]
1735 fn test_invalid_regex() {
1736 let src = "package(/)/)";
1737 let mut errors = parse_err(src);
1738 assert_eq!(1, errors.len());
1739 let error = errors.remove(0);
1740 assert!(
1741 matches!(error, ParseSingleError::InvalidRegex { span, .. } if span == (9, 1).into())
1742 );
1743
1744 let src = "package(/foo(ab/)";
1746 let mut errors = parse_err(src);
1747 assert_eq!(1, errors.len());
1748 let error = errors.remove(0);
1749 let (span, message) = match error {
1750 ParseSingleError::InvalidRegex { span, message } => (span, message),
1751 other => panic!("expected invalid regex with details, found {other}"),
1752 };
1753 assert_eq!(span, (12, 1).into(), "span matches");
1754 assert_eq!(message, "unclosed group");
1755 }
1756
1757 #[test]
1758 fn test_invalid_glob() {
1759 let src = "package(#)";
1760 let mut errors = parse_err(src);
1761 assert_eq!(1, errors.len());
1762 let error = errors.remove(0);
1763 assert_error!(error, InvalidString, 9, 0);
1764
1765 let src = "package(#foo[)";
1766 let mut errors = parse_err(src);
1767 assert_eq!(1, errors.len());
1768 let error = errors.remove(0);
1769 let (span, error) = match error {
1770 ParseSingleError::InvalidGlob { span, error } => (span, error),
1771 other => panic!("expected InvalidGlob with details, found {other}"),
1772 };
1773 assert_eq!(span, (9, 4).into(), "span matches");
1774 assert_eq!(error.to_string(), "unclosed character class; missing ']'");
1775 }
1776
1777 #[test]
1778 fn test_invalid_platform() {
1779 let src = "platform(foo)";
1780 let mut errors = parse_err(src);
1781 assert_eq!(1, errors.len());
1782 let error = errors.remove(0);
1783 assert_error!(error, InvalidPlatformArgument, 9, 3);
1784
1785 let src = "platform( bar\\t)";
1786 let mut errors = parse_err(src);
1787 assert_eq!(1, errors.len());
1788 let error = errors.remove(0);
1789 assert_error!(error, InvalidPlatformArgument, 9, 8);
1790 }
1791
1792 #[test]
1793 fn test_missing_close_regex() {
1794 let src = "package(/aaa)";
1795 let mut errors = parse_err(src);
1796 assert_eq!(1, errors.len());
1797 let error = errors.remove(0);
1798 assert_error!(error, ExpectedCloseRegex, 12, 0);
1799 }
1800
1801 #[test]
1802 fn test_unexpected_argument() {
1803 let src = "all(aaa)";
1804 let mut errors = parse_err(src);
1805 assert_eq!(1, errors.len());
1806 let error = errors.remove(0);
1807 assert_error!(error, UnexpectedArgument, 4, 3);
1808 }
1809
1810 #[test]
1811 fn test_expected_expr() {
1812 let src = "all() + ";
1813 let mut errors = parse_err(src);
1814 assert_eq!(1, errors.len());
1815 let error = errors.remove(0);
1816 assert_error!(error, ExpectedExpr, 7, 1);
1817 }
1818
1819 #[test]
1820 fn test_misplaced_binary_op() {
1821 let mut errors = parse_err("and(a, b)");
1823 assert_eq!(2, errors.len(), "errors: {errors:?}");
1824 match errors.remove(0) {
1825 ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1826 assert_eq!(op, "and");
1827 assert_eq!(suggest, "and");
1828 assert_eq!(span, (0, 3).into());
1829 }
1830 other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1831 }
1832 let error = errors.remove(0);
1833 assert_error!(error, ExpectedEndOfExpression, 3, 6);
1834
1835 let mut errors = parse_err("or(a, b)");
1837 assert_eq!(2, errors.len(), "errors: {errors:?}");
1838 match errors.remove(0) {
1839 ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1840 assert_eq!(op, "or");
1841 assert_eq!(suggest, "or");
1842 assert_eq!(span, (0, 2).into());
1843 }
1844 other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1845 }
1846
1847 let errors = parse_err("AND(test(foo))");
1851 match &errors[0] {
1852 ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1853 assert_eq!(*op, "AND");
1854 assert_eq!(*suggest, "and");
1855 assert_eq!(*span, (0, 3).into());
1856 }
1857 other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1858 }
1859
1860 let errors = parse_err("OR(test(foo))");
1861 match &errors[0] {
1862 ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1863 assert_eq!(*op, "OR");
1864 assert_eq!(*suggest, "or");
1865 assert_eq!(*span, (0, 2).into());
1866 }
1867 other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1868 }
1869
1870 let errors = parse_err("all() and or all()");
1873 let positioned = errors
1874 .iter()
1875 .find(|e| matches!(e, ParseSingleError::ExprFoundBinaryOp { op: "or", .. }));
1876 assert!(
1877 positioned.is_some(),
1878 "expected ExprFoundBinaryOp for `or` in RHS position; got: {errors:?}"
1879 );
1880
1881 let errors = parse_err("(and all())");
1883 let positioned = errors
1884 .iter()
1885 .find(|e| matches!(e, ParseSingleError::ExprFoundBinaryOp { op: "and", .. }));
1886 assert!(
1887 positioned.is_some(),
1888 "expected ExprFoundBinaryOp inside parens; got: {errors:?}"
1889 );
1890
1891 let mut errors = parse_err("&&(a, b)");
1895 assert_eq!(2, errors.len(), "errors: {errors:?}");
1896 match errors.remove(0) {
1897 ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1898 assert_eq!(op, "&&");
1899 assert_eq!(suggest, "&");
1900 assert_eq!(span, (0, 2).into());
1901 }
1902 other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1903 }
1904 let error = errors.remove(0);
1905 assert_error!(error, ExpectedEndOfExpression, 2, 6);
1906
1907 let mut errors = parse_err("||(a, b)");
1908 assert_eq!(2, errors.len(), "errors: {errors:?}");
1909 match errors.remove(0) {
1910 ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1911 assert_eq!(op, "||");
1912 assert_eq!(suggest, "|");
1913 assert_eq!(span, (0, 2).into());
1914 }
1915 other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1916 }
1917 let error = errors.remove(0);
1918 assert_error!(error, ExpectedEndOfExpression, 2, 6);
1919 }
1920
1921 #[test]
1922 fn test_misplaced_unary_op() {
1923 let mut errors = parse_err("NOT(all())");
1924 assert_eq!(errors.len(), 2, "errors: {errors:?}");
1925 match errors.remove(0) {
1926 ParseSingleError::ExprFoundUnaryOp { op, suggest, span } => {
1927 assert_eq!(op, "NOT");
1928 assert_eq!(suggest, "not");
1929 assert_eq!(span, (0, 3).into());
1930 }
1931 other => panic!("expected ExprFoundUnaryOp, got: {other:?}"),
1932 }
1933 let error = errors.remove(0);
1934 assert_error!(error, ExpectedEndOfExpression, 3, 7);
1935
1936 let errors = parse_err("all() and NOT all()");
1939 let positioned = errors
1940 .iter()
1941 .find(|e| matches!(e, ParseSingleError::ExprFoundUnaryOp { op: "NOT", .. }));
1942 assert!(
1943 positioned.is_some(),
1944 "expected ExprFoundUnaryOp for `NOT` in RHS position; got: {errors:?}"
1945 );
1946 }
1947
1948 #[test]
1949 fn test_keyword_word_boundary() {
1950 let mut errors = parse_err("notall()");
1954 assert_eq!(2, errors.len(), "errors: {errors:?}");
1955 let error = errors.remove(0);
1956 assert_error!(error, ExpectedExpr, 0, 8);
1957 let error = errors.remove(0);
1958 assert_error!(error, ExpectedEndOfExpression, 0, 8);
1959
1960 let mut errors = parse_err("nottest(foo)");
1962 assert_eq!(2, errors.len(), "errors: {errors:?}");
1963 let error = errors.remove(0);
1964 assert_error!(error, ExpectedExpr, 0, 12);
1965 let error = errors.remove(0);
1966 assert_error!(error, ExpectedEndOfExpression, 0, 12);
1967
1968 let mut errors = parse_err("all() andall()");
1972 assert_eq!(1, errors.len(), "errors: {errors:?}");
1973 let error = errors.remove(0);
1974 assert_error!(error, ExpectedEndOfExpression, 5, 9);
1975
1976 let mut errors = parse_err("all() ornone()");
1977 assert_eq!(1, errors.len(), "errors: {errors:?}");
1978 let error = errors.remove(0);
1979 assert_error!(error, ExpectedEndOfExpression, 5, 9);
1980 }
1981
1982 #[test]
1983 fn test_expected_eof() {
1984 let src = "all() blabla";
1985 let mut errors = parse_err(src);
1986 assert_eq!(1, errors.len());
1987 let error = errors.remove(0);
1988 assert_error!(error, ExpectedEndOfExpression, 5, 7);
1989 }
1990
1991 #[test]
1992 fn test_missing_argument() {
1993 let src = "test()";
1994 let mut errors = parse_err(src);
1995 assert_eq!(1, errors.len());
1996 let error = errors.remove(0);
1997 assert_error!(error, InvalidString, 5, 0);
1998 }
1999
2000 #[test]
2001 fn test_unexpected_comma() {
2002 let src = "test(aa, )";
2003 let mut errors = parse_err(src);
2004 assert_eq!(1, errors.len());
2005 let error = errors.remove(0);
2006 assert_error!(error, UnexpectedComma, 7, 0);
2007 }
2008
2009 #[test]
2010 fn test_complex_error() {
2011 let src = "all) + package(/not) - deps(expr none)";
2012 let mut errors = parse_err(src);
2013 assert_eq!(2, errors.len(), "{errors:?}");
2014 let error = errors.remove(0);
2015 assert_error!(error, ExpectedOpenParenthesis, 3, 0);
2016 let error = errors.remove(0);
2017 assert_error!(error, ExpectedCloseRegex, 19, 0);
2018 }
2019
2020 #[test_strategy::proptest]
2021 fn proptest_expr_roundtrip(#[strategy(ParsedExpr::strategy())] expr: ParsedExpr<()>) {
2022 let expr_string = expr.to_string();
2023 eprintln!("expr string: {expr_string}");
2024 let expr_2 = parse(&expr_string).drop_source_span();
2025
2026 assert_eq!(expr, expr_2, "exprs must roundtrip");
2027 }
2028
2029 #[track_caller]
2030 fn assert_eq_both_ways(expr: &ParsedExpr, string: &str) {
2031 assert_eq!(expr, &parse(string));
2032 assert_eq!(format!("{expr}"), string);
2033 }
2034}