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