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