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, 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#[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 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 ' '.void(),
351 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
372fn 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
455pub(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 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
534fn 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,
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 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 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 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 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#[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 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 let op = alt(("||", "OR ")).parse_next(input)?;
818 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#[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 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
935pub(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 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 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 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 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 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 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 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 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 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 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}