1use crate::error::{ParseError, ParseErrorKind::*};
2use std::fmt;
3use std::iter;
4use std::str::{self, FromStr};
5
6/// A cfg expression.
7#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
8pub 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)]
17pub 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)]
25enum Token<'a> {
26 LeftParen,
27 RightParen,
28 Ident(&'a str),
29 Comma,
30 Equals,
31 String(&'a str),
32}
33
34#[derive(Clone)]
35struct Tokenizer<'a> {
36 s: iter::Peekable<str::CharIndices<'a>>,
37 orig: &'a str,
38}
39
40struct Parser<'a> {
41 t: Tokenizer<'a>,
42}
43
44impl FromStr for Cfg {
45 type Err = ParseError;
46
47 fn from_str(s: &str) -> Result<Cfg, Self::Err> {
48 let mut p: Parser<'_> = Parser::new(s);
49 let e: Cfg = p.cfg()?;
50 if let Some(rest: &str) = p.rest() {
51 return Err(ParseError::new(
52 p.t.orig,
53 kind:UnterminatedExpression(rest.to_string()),
54 ));
55 }
56 Ok(e)
57 }
58}
59
60impl fmt::Display for Cfg {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match *self {
63 Cfg::Name(ref s: &String) => s.fmt(f),
64 Cfg::KeyPair(ref k: &String, ref v: &String) => write!(f, "{} = \"{}\"", k, v),
65 }
66 }
67}
68
69impl CfgExpr {
70 /// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
71 pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
72 if key.starts_with("cfg(") && key.ends_with(')') {
73 let cfg: &str = &key[4..key.len() - 1];
74
75 CfgExpr::from_str(cfg)
76 .ok()
77 .map(|ce| ce.matches(target_cfg))
78 .unwrap_or(default:false)
79 } else {
80 false
81 }
82 }
83
84 pub fn matches(&self, cfg: &[Cfg]) -> bool {
85 match *self {
86 CfgExpr::Not(ref e: &Box) => !e.matches(cfg),
87 CfgExpr::All(ref e: &Vec) => e.iter().all(|e: &CfgExpr| e.matches(cfg)),
88 CfgExpr::Any(ref e: &Vec) => e.iter().any(|e: &CfgExpr| e.matches(cfg)),
89 CfgExpr::Value(ref e: &Cfg) => cfg.contains(e),
90 }
91 }
92}
93
94impl FromStr for CfgExpr {
95 type Err = ParseError;
96
97 fn from_str(s: &str) -> Result<CfgExpr, Self::Err> {
98 let mut p: Parser<'_> = Parser::new(s);
99 let e: CfgExpr = p.expr()?;
100 if let Some(rest: &str) = p.rest() {
101 return Err(ParseError::new(
102 p.t.orig,
103 kind:UnterminatedExpression(rest.to_string()),
104 ));
105 }
106 Ok(e)
107 }
108}
109
110impl fmt::Display for CfgExpr {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match *self {
113 CfgExpr::Not(ref e: &Box) => write!(f, "not({})", e),
114 CfgExpr::All(ref e: &Vec) => write!(f, "all({})", CommaSep(e)),
115 CfgExpr::Any(ref e: &Vec) => write!(f, "any({})", CommaSep(e)),
116 CfgExpr::Value(ref e: &Cfg) => write!(f, "{}", e),
117 }
118 }
119}
120
121struct CommaSep<'a, T>(&'a [T]);
122
123impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 for (i: usize, v: &T) in self.0.iter().enumerate() {
126 if i > 0 {
127 write!(f, ", ")?;
128 }
129 write!(f, "{}", v)?;
130 }
131 Ok(())
132 }
133}
134
135impl<'a> 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
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
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
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
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.
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
262impl<'a> Iterator for Tokenizer<'a> {
263 type Item = Result<Token<'a>, ParseError>;
264
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
300fn is_ident_start(ch: char) -> bool {
301 ch == '_' || ch.is_ascii_alphabetic()
302}
303
304fn is_ident_rest(ch: char) -> bool {
305 is_ident_start(ch) || ch.is_ascii_digit()
306}
307
308impl<'a> Token<'a> {
309 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