1 use std::{error::Error, fmt};
2 
3 /// An error related to parsing of a cfg expression
4 #[derive(Debug, PartialEq)]
5 pub struct ParseError {
6     /// The string that was parsed
7     pub original: String,
8     /// The range of characters in the original string that result
9     /// in this error
10     pub span: std::ops::Range<usize>,
11     /// The specific reason for the error
12     pub reason: Reason,
13 }
14 
15 /// The particular reason for a `ParseError`
16 #[derive(Debug, PartialEq)]
17 pub enum Reason {
18     /// not() takes exactly 1 predicate, unlike all() and any()
19     InvalidNot(usize),
20     /// The characters are not valid in an cfg expression
21     InvalidCharacters,
22     /// An opening parens was unmatched with a closing parens
23     UnclosedParens,
24     /// A closing parens was unmatched with an opening parens
25     UnopenedParens,
26     /// An opening quotes was unmatched with a closing quotes
27     UnclosedQuotes,
28     /// A closing quotes was unmatched with an opening quotes
29     UnopenedQuotes,
30     /// The expression does not contain any valid terms
31     Empty,
32     /// Found an unexpected term, which wasn't one of the expected terms that
33     /// is listed
34     Unexpected(&'static [&'static str]),
35     /// Failed to parse an integer value
36     InvalidInteger,
37     /// The root cfg() may only contain a single predicate
38     MultipleRootPredicates,
39     /// An element was not part of the builtin information in rustc
40     UnknownBuiltin,
41 }
42 
43 impl fmt::Display for ParseError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result44     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45         f.write_str(&self.original)?;
46         f.write_str("\n")?;
47 
48         for _ in 0..self.span.start {
49             f.write_str(" ")?;
50         }
51 
52         // Mismatched parens/quotes have a slightly different output
53         // than the other errors
54         match &self.reason {
55             r @ Reason::UnclosedParens | r @ Reason::UnclosedQuotes => {
56                 f.write_fmt(format_args!("- {}", r))
57             }
58             r @ Reason::UnopenedParens | r @ Reason::UnopenedQuotes => {
59                 f.write_fmt(format_args!("^ {}", r))
60             }
61             other => {
62                 for _ in self.span.start..self.span.end {
63                     f.write_str("^")?;
64                 }
65 
66                 f.write_fmt(format_args!(" {}", other))
67             }
68         }
69     }
70 }
71 
72 impl fmt::Display for Reason {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result73     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74         use Reason::{
75             Empty, InvalidCharacters, InvalidInteger, InvalidNot, MultipleRootPredicates,
76             UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin, UnopenedParens,
77             UnopenedQuotes,
78         };
79 
80         match self {
81             InvalidCharacters => f.write_str("invalid character(s)"),
82             UnclosedParens => f.write_str("unclosed parens"),
83             UnopenedParens => f.write_str("unopened parens"),
84             UnclosedQuotes => f.write_str("unclosed quotes"),
85             UnopenedQuotes => f.write_str("unopened quotes"),
86             Empty => f.write_str("empty expression"),
87             Unexpected(expected) => {
88                 if expected.len() > 1 {
89                     f.write_str("expected one of ")?;
90 
91                     for (i, exp) in expected.iter().enumerate() {
92                         f.write_fmt(format_args!("{}`{}`", if i > 0 { ", " } else { "" }, exp))?;
93                     }
94                     f.write_str(" here")
95                 } else if !expected.is_empty() {
96                     f.write_fmt(format_args!("expected a `{}` here", expected[0]))
97                 } else {
98                     f.write_str("the term was not expected here")
99                 }
100             }
101             InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {}", np)),
102             InvalidInteger => f.write_str("invalid integer"),
103             MultipleRootPredicates => f.write_str("multiple root predicates"),
104             UnknownBuiltin => f.write_str("unknown built-in"),
105         }
106     }
107 }
108 
109 impl Error for ParseError {
description(&self) -> &str110     fn description(&self) -> &str {
111         use Reason::{
112             Empty, InvalidCharacters, InvalidInteger, InvalidNot, MultipleRootPredicates,
113             UnclosedParens, UnclosedQuotes, Unexpected, UnknownBuiltin, UnopenedParens,
114             UnopenedQuotes,
115         };
116 
117         match self.reason {
118             InvalidCharacters => "invalid character(s)",
119             UnclosedParens => "unclosed parens",
120             UnopenedParens => "unopened parens",
121             UnclosedQuotes => "unclosed quotes",
122             UnopenedQuotes => "unopened quotes",
123             Empty => "empty expression",
124             Unexpected(_) => "unexpected term",
125             InvalidNot(_) => "not() takes 1 predicate",
126             InvalidInteger => "invalid integer",
127             MultipleRootPredicates => "multiple root predicates",
128             UnknownBuiltin => "unknown built-in",
129         }
130     }
131 }
132