1 use crate::error::{ParseError, ParseErrorKind::*};
2 use std::fmt;
3 use std::iter;
4 use std::str::{self, FromStr};
5 
6 /// A cfg expression.
7 #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
8 pub enum CfgExpr {
9     Not(Box<CfgExpr>),
10     All(Vec<CfgExpr>),
11     Any(Vec<CfgExpr>),
12     Value(Cfg),
13 }
14 
15 /// A cfg value.
16 #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
17 pub enum Cfg {
18     /// A named cfg value, like `unix`.
19     Name(String),
20     /// A key/value cfg pair, like `target_os = "linux"`.
21     KeyPair(String, String),
22 }
23 
24 #[derive(PartialEq)]
25 enum Token<'a> {
26     LeftParen,
27     RightParen,
28     Ident(&'a str),
29     Comma,
30     Equals,
31     String(&'a str),
32 }
33 
34 #[derive(Clone)]
35 struct Tokenizer<'a> {
36     s: iter::Peekable<str::CharIndices<'a>>,
37     orig: &'a str,
38 }
39 
40 struct Parser<'a> {
41     t: Tokenizer<'a>,
42 }
43 
44 impl FromStr for Cfg {
45     type Err = ParseError;
46 
from_str(s: &str) -> Result<Cfg, Self::Err>47     fn from_str(s: &str) -> Result<Cfg, Self::Err> {
48         let mut p = Parser::new(s);
49         let e = p.cfg()?;
50         if let Some(rest) = p.rest() {
51             return Err(ParseError::new(
52                 p.t.orig,
53                 UnterminatedExpression(rest.to_string()),
54             ));
55         }
56         Ok(e)
57     }
58 }
59 
60 impl fmt::Display for Cfg {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result61     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62         match *self {
63             Cfg::Name(ref s) => s.fmt(f),
64             Cfg::KeyPair(ref k, ref v) => write!(f, "{} = \"{}\"", k, v),
65         }
66     }
67 }
68 
69 impl CfgExpr {
70     /// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
matches_key(key: &str, target_cfg: &[Cfg]) -> bool71     pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
72         if key.starts_with("cfg(") && key.ends_with(')') {
73             let cfg = &key[4..key.len() - 1];
74 
75             CfgExpr::from_str(cfg)
76                 .ok()
77                 .map(|ce| ce.matches(target_cfg))
78                 .unwrap_or(false)
79         } else {
80             false
81         }
82     }
83 
matches(&self, cfg: &[Cfg]) -> bool84     pub fn matches(&self, cfg: &[Cfg]) -> bool {
85         match *self {
86             CfgExpr::Not(ref e) => !e.matches(cfg),
87             CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg)),
88             CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg)),
89             CfgExpr::Value(ref e) => cfg.contains(e),
90         }
91     }
92 }
93 
94 impl FromStr for CfgExpr {
95     type Err = ParseError;
96 
from_str(s: &str) -> Result<CfgExpr, Self::Err>97     fn from_str(s: &str) -> Result<CfgExpr, Self::Err> {
98         let mut p = Parser::new(s);
99         let e = p.expr()?;
100         if let Some(rest) = p.rest() {
101             return Err(ParseError::new(
102                 p.t.orig,
103                 UnterminatedExpression(rest.to_string()),
104             ));
105         }
106         Ok(e)
107     }
108 }
109 
110 impl fmt::Display for CfgExpr {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result111     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112         match *self {
113             CfgExpr::Not(ref e) => write!(f, "not({})", e),
114             CfgExpr::All(ref e) => write!(f, "all({})", CommaSep(e)),
115             CfgExpr::Any(ref e) => write!(f, "any({})", CommaSep(e)),
116             CfgExpr::Value(ref e) => write!(f, "{}", e),
117         }
118     }
119 }
120 
121 struct CommaSep<'a, T>(&'a [T]);
122 
123 impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result124     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125         for (i, v) in self.0.iter().enumerate() {
126             if i > 0 {
127                 write!(f, ", ")?;
128             }
129             write!(f, "{}", v)?;
130         }
131         Ok(())
132     }
133 }
134 
135 impl<'a> Parser<'a> {
new(s: &'a str) -> Parser<'a>136     fn new(s: &'a str) -> Parser<'a> {
137         Parser {
138             t: Tokenizer {
139                 s: s.char_indices().peekable(),
140                 orig: s,
141             },
142         }
143     }
144 
expr(&mut self) -> Result<CfgExpr, ParseError>145     fn expr(&mut self) -> Result<CfgExpr, ParseError> {
146         match self.peek() {
147             Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => {
148                 self.t.next();
149                 let mut e = Vec::new();
150                 self.eat(&Token::LeftParen)?;
151                 while !self.r#try(&Token::RightParen) {
152                     e.push(self.expr()?);
153                     if !self.r#try(&Token::Comma) {
154                         self.eat(&Token::RightParen)?;
155                         break;
156                     }
157                 }
158                 if op == "all" {
159                     Ok(CfgExpr::All(e))
160                 } else {
161                     Ok(CfgExpr::Any(e))
162                 }
163             }
164             Some(Ok(Token::Ident("not"))) => {
165                 self.t.next();
166                 self.eat(&Token::LeftParen)?;
167                 let e = self.expr()?;
168                 self.eat(&Token::RightParen)?;
169                 Ok(CfgExpr::Not(Box::new(e)))
170             }
171             Some(Ok(..)) => self.cfg().map(CfgExpr::Value),
172             Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
173             None => Err(ParseError::new(
174                 self.t.orig,
175                 IncompleteExpr("start of a cfg expression"),
176             )),
177         }
178     }
179 
cfg(&mut self) -> Result<Cfg, ParseError>180     fn cfg(&mut self) -> Result<Cfg, ParseError> {
181         match self.t.next() {
182             Some(Ok(Token::Ident(name))) => {
183                 let e = if self.r#try(&Token::Equals) {
184                     let val = match self.t.next() {
185                         Some(Ok(Token::String(s))) => s,
186                         Some(Ok(t)) => {
187                             return Err(ParseError::new(
188                                 self.t.orig,
189                                 UnexpectedToken {
190                                     expected: "a string",
191                                     found: t.classify(),
192                                 },
193                             ))
194                         }
195                         Some(Err(e)) => return Err(e),
196                         None => {
197                             return Err(ParseError::new(self.t.orig, IncompleteExpr("a string")))
198                         }
199                     };
200                     Cfg::KeyPair(name.to_string(), val.to_string())
201                 } else {
202                     Cfg::Name(name.to_string())
203                 };
204                 Ok(e)
205             }
206             Some(Ok(t)) => Err(ParseError::new(
207                 self.t.orig,
208                 UnexpectedToken {
209                     expected: "identifier",
210                     found: t.classify(),
211                 },
212             )),
213             Some(Err(e)) => Err(e),
214             None => Err(ParseError::new(self.t.orig, IncompleteExpr("identifier"))),
215         }
216     }
217 
peek(&mut self) -> Option<Result<Token<'a>, ParseError>>218     fn peek(&mut self) -> Option<Result<Token<'a>, ParseError>> {
219         self.t.clone().next()
220     }
221 
222     fn r#try(&mut self, token: &Token<'a>) -> bool {
223         match self.peek() {
224             Some(Ok(ref t)) if token == t => {}
225             _ => return false,
226         }
227         self.t.next();
228         true
229     }
230 
eat(&mut self, token: &Token<'a>) -> Result<(), ParseError>231     fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> {
232         match self.t.next() {
233             Some(Ok(ref t)) if token == t => Ok(()),
234             Some(Ok(t)) => Err(ParseError::new(
235                 self.t.orig,
236                 UnexpectedToken {
237                     expected: token.classify(),
238                     found: t.classify(),
239                 },
240             )),
241             Some(Err(e)) => Err(e),
242             None => Err(ParseError::new(
243                 self.t.orig,
244                 IncompleteExpr(token.classify()),
245             )),
246         }
247     }
248 
249     /// Returns the rest of the input from the current location.
rest(&self) -> Option<&str>250     fn rest(&self) -> Option<&str> {
251         let mut s = self.t.s.clone();
252         loop {
253             match s.next() {
254                 Some((_, ' ')) => {}
255                 Some((start, _ch)) => return Some(&self.t.orig[start..]),
256                 None => return None,
257             }
258         }
259     }
260 }
261 
262 impl<'a> Iterator for Tokenizer<'a> {
263     type Item = Result<Token<'a>, ParseError>;
264 
next(&mut self) -> Option<Result<Token<'a>, ParseError>>265     fn next(&mut self) -> Option<Result<Token<'a>, ParseError>> {
266         loop {
267             match self.s.next() {
268                 Some((_, ' ')) => {}
269                 Some((_, '(')) => return Some(Ok(Token::LeftParen)),
270                 Some((_, ')')) => return Some(Ok(Token::RightParen)),
271                 Some((_, ',')) => return Some(Ok(Token::Comma)),
272                 Some((_, '=')) => return Some(Ok(Token::Equals)),
273                 Some((start, '"')) => {
274                     while let Some((end, ch)) = self.s.next() {
275                         if ch == '"' {
276                             return Some(Ok(Token::String(&self.orig[start + 1..end])));
277                         }
278                     }
279                     return Some(Err(ParseError::new(self.orig, UnterminatedString)));
280                 }
281                 Some((start, ch)) if is_ident_start(ch) => {
282                     while let Some(&(end, ch)) = self.s.peek() {
283                         if !is_ident_rest(ch) {
284                             return Some(Ok(Token::Ident(&self.orig[start..end])));
285                         } else {
286                             self.s.next();
287                         }
288                     }
289                     return Some(Ok(Token::Ident(&self.orig[start..])));
290                 }
291                 Some((_, ch)) => {
292                     return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));
293                 }
294                 None => return None,
295             }
296         }
297     }
298 }
299 
is_ident_start(ch: char) -> bool300 fn is_ident_start(ch: char) -> bool {
301     ch == '_' || ch.is_ascii_alphabetic()
302 }
303 
is_ident_rest(ch: char) -> bool304 fn is_ident_rest(ch: char) -> bool {
305     is_ident_start(ch) || ch.is_ascii_digit()
306 }
307 
308 impl<'a> Token<'a> {
classify(&self) -> &'static str309     fn classify(&self) -> &'static str {
310         match *self {
311             Token::LeftParen => "`(`",
312             Token::RightParen => "`)`",
313             Token::Ident(..) => "an identifier",
314             Token::Comma => "`,`",
315             Token::Equals => "`=`",
316             Token::String(..) => "a string",
317         }
318     }
319 }
320