1use crate::error::{ParseError, ParseErrorKind::*};
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::iter;
5use std::str::{self, FromStr};
6
7#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
9pub enum CfgExpr {
10 Not(Box<CfgExpr>),
11 All(Vec<CfgExpr>),
12 Any(Vec<CfgExpr>),
13 Value(Cfg),
14}
15
16#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
18pub enum Cfg {
19 Name(Ident),
21 KeyPair(Ident, String),
23}
24
25#[derive(Eq, Ord, PartialOrd, Clone, Debug)]
27pub struct Ident {
28 pub name: String,
30 pub raw: bool,
35}
36
37#[derive(PartialEq)]
38enum Token<'a> {
39 LeftParen,
40 RightParen,
41 Ident(bool, &'a str),
42 Comma,
43 Equals,
44 String(&'a str),
45}
46
47pub(crate) const KEYWORDS: &[&str; 2] = &["true", "false"];
54
55#[derive(Clone)]
56struct Tokenizer<'a> {
57 s: iter::Peekable<str::CharIndices<'a>>,
58 orig: &'a str,
59}
60
61struct Parser<'a> {
62 t: Tokenizer<'a>,
63}
64
65impl Ident {
66 pub fn as_str(&self) -> &str {
67 &self.name
68 }
69}
70
71impl Hash for Ident {
72 fn hash<H: Hasher>(&self, state: &mut H) {
73 self.name.hash(state);
74 }
75}
76
77impl PartialEq<str> for Ident {
78 fn eq(&self, other: &str) -> bool {
79 self.name == other
80 }
81}
82
83impl PartialEq<&str> for Ident {
84 fn eq(&self, other: &&str) -> bool {
85 self.name == *other
86 }
87}
88
89impl PartialEq<Ident> for Ident {
90 fn eq(&self, other: &Ident) -> bool {
91 self.name == other.name
92 }
93}
94
95impl fmt::Display for Ident {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 if self.raw {
98 f.write_str("r#")?;
99 }
100 f.write_str(&*self.name)
101 }
102}
103
104impl FromStr for Cfg {
105 type Err = ParseError;
106
107 fn from_str(s: &str) -> Result<Cfg, Self::Err> {
108 let mut p = Parser::new(s);
109 let e = p.cfg()?;
110 if let Some(rest) = p.rest() {
111 return Err(ParseError::new(
112 p.t.orig,
113 UnterminatedExpression(rest.to_string()),
114 ));
115 }
116 Ok(e)
117 }
118}
119
120impl fmt::Display for Cfg {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match *self {
123 Cfg::Name(ref s) => s.fmt(f),
124 Cfg::KeyPair(ref k, ref v) => write!(f, "{} = \"{}\"", k, v),
125 }
126 }
127}
128
129impl CfgExpr {
130 pub fn matches_key(key: &str, target_cfg: &[Cfg]) -> bool {
132 if key.starts_with("cfg(") && key.ends_with(')') {
133 let cfg = &key[4..key.len() - 1];
134
135 CfgExpr::from_str(cfg)
136 .ok()
137 .map(|ce| ce.matches(target_cfg))
138 .unwrap_or(false)
139 } else {
140 false
141 }
142 }
143
144 pub fn matches(&self, cfg: &[Cfg]) -> bool {
145 match *self {
146 CfgExpr::Not(ref e) => !e.matches(cfg),
147 CfgExpr::All(ref e) => e.iter().all(|e| e.matches(cfg)),
148 CfgExpr::Any(ref e) => e.iter().any(|e| e.matches(cfg)),
149 CfgExpr::Value(ref e) => cfg.contains(e),
150 }
151 }
152}
153
154impl FromStr for CfgExpr {
155 type Err = ParseError;
156
157 fn from_str(s: &str) -> Result<CfgExpr, Self::Err> {
158 let mut p = Parser::new(s);
159 let e = p.expr()?;
160 if let Some(rest) = p.rest() {
161 return Err(ParseError::new(
162 p.t.orig,
163 UnterminatedExpression(rest.to_string()),
164 ));
165 }
166 Ok(e)
167 }
168}
169
170impl fmt::Display for CfgExpr {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 match *self {
173 CfgExpr::Not(ref e) => write!(f, "not({})", e),
174 CfgExpr::All(ref e) => write!(f, "all({})", CommaSep(e)),
175 CfgExpr::Any(ref e) => write!(f, "any({})", CommaSep(e)),
176 CfgExpr::Value(ref e) => write!(f, "{}", e),
177 }
178 }
179}
180
181struct CommaSep<'a, T>(&'a [T]);
182
183impl<'a, T: fmt::Display> fmt::Display for CommaSep<'a, T> {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 for (i, v) in self.0.iter().enumerate() {
186 if i > 0 {
187 write!(f, ", ")?;
188 }
189 write!(f, "{}", v)?;
190 }
191 Ok(())
192 }
193}
194
195impl<'a> Parser<'a> {
196 fn new(s: &'a str) -> Parser<'a> {
197 Parser {
198 t: Tokenizer {
199 s: s.char_indices().peekable(),
200 orig: s,
201 },
202 }
203 }
204
205 fn expr(&mut self) -> Result<CfgExpr, ParseError> {
206 match self.peek() {
207 Some(Ok(Token::Ident(false, op @ "all")))
208 | Some(Ok(Token::Ident(false, op @ "any"))) => {
209 self.t.next();
210 let mut e = Vec::new();
211 self.eat(&Token::LeftParen)?;
212 while !self.r#try(&Token::RightParen) {
213 e.push(self.expr()?);
214 if !self.r#try(&Token::Comma) {
215 self.eat(&Token::RightParen)?;
216 break;
217 }
218 }
219 if op == "all" {
220 Ok(CfgExpr::All(e))
221 } else {
222 Ok(CfgExpr::Any(e))
223 }
224 }
225 Some(Ok(Token::Ident(false, "not"))) => {
226 self.t.next();
227 self.eat(&Token::LeftParen)?;
228 let e = self.expr()?;
229 self.eat(&Token::RightParen)?;
230 Ok(CfgExpr::Not(Box::new(e)))
231 }
232 Some(Ok(..)) => self.cfg().map(CfgExpr::Value),
233 Some(Err(..)) => Err(self.t.next().unwrap().err().unwrap()),
234 None => Err(ParseError::new(
235 self.t.orig,
236 IncompleteExpr("start of a cfg expression"),
237 )),
238 }
239 }
240
241 fn cfg(&mut self) -> Result<Cfg, ParseError> {
242 match self.t.next() {
243 Some(Ok(Token::Ident(raw, name))) => {
244 let e = if self.r#try(&Token::Equals) {
245 let val = match self.t.next() {
246 Some(Ok(Token::String(s))) => s,
247 Some(Ok(t)) => {
248 return Err(ParseError::new(
249 self.t.orig,
250 UnexpectedToken {
251 expected: "a string",
252 found: t.classify(),
253 },
254 ))
255 }
256 Some(Err(e)) => return Err(e),
257 None => {
258 return Err(ParseError::new(self.t.orig, IncompleteExpr("a string")))
259 }
260 };
261 Cfg::KeyPair(
262 Ident {
263 name: name.to_string(),
264 raw,
265 },
266 val.to_string(),
267 )
268 } else {
269 Cfg::Name(Ident {
270 name: name.to_string(),
271 raw,
272 })
273 };
274 Ok(e)
275 }
276 Some(Ok(t)) => Err(ParseError::new(
277 self.t.orig,
278 UnexpectedToken {
279 expected: "identifier",
280 found: t.classify(),
281 },
282 )),
283 Some(Err(e)) => Err(e),
284 None => Err(ParseError::new(self.t.orig, IncompleteExpr("identifier"))),
285 }
286 }
287
288 fn peek(&mut self) -> Option<Result<Token<'a>, ParseError>> {
289 self.t.clone().next()
290 }
291
292 fn r#try(&mut self, token: &Token<'a>) -> bool {
293 match self.peek() {
294 Some(Ok(ref t)) if token == t => {}
295 _ => return false,
296 }
297 self.t.next();
298 true
299 }
300
301 fn eat(&mut self, token: &Token<'a>) -> Result<(), ParseError> {
302 match self.t.next() {
303 Some(Ok(ref t)) if token == t => Ok(()),
304 Some(Ok(t)) => Err(ParseError::new(
305 self.t.orig,
306 UnexpectedToken {
307 expected: token.classify(),
308 found: t.classify(),
309 },
310 )),
311 Some(Err(e)) => Err(e),
312 None => Err(ParseError::new(
313 self.t.orig,
314 IncompleteExpr(token.classify()),
315 )),
316 }
317 }
318
319 fn rest(&self) -> Option<&str> {
321 let mut s = self.t.s.clone();
322 loop {
323 match s.next() {
324 Some((_, ' ')) => {}
325 Some((start, _ch)) => return Some(&self.t.orig[start..]),
326 None => return None,
327 }
328 }
329 }
330}
331
332impl<'a> Iterator for Tokenizer<'a> {
333 type Item = Result<Token<'a>, ParseError>;
334
335 fn next(&mut self) -> Option<Result<Token<'a>, ParseError>> {
336 loop {
337 match self.s.next() {
338 Some((_, ' ')) => {}
339 Some((_, '(')) => return Some(Ok(Token::LeftParen)),
340 Some((_, ')')) => return Some(Ok(Token::RightParen)),
341 Some((_, ',')) => return Some(Ok(Token::Comma)),
342 Some((_, '=')) => return Some(Ok(Token::Equals)),
343 Some((start, '"')) => {
344 while let Some((end, ch)) = self.s.next() {
345 if ch == '"' {
346 return Some(Ok(Token::String(&self.orig[start + 1..end])));
347 }
348 }
349 return Some(Err(ParseError::new(self.orig, UnterminatedString)));
350 }
351 Some((start, ch)) if is_ident_start(ch) => {
352 let (start, raw) = if ch == 'r' {
353 if let Some(&(_pos, '#')) = self.s.peek() {
354 self.s.next();
356 if let Some((start, ch)) = self.s.next() {
357 if is_ident_start(ch) {
358 (start, true)
359 } else {
360 return Some(Err(ParseError::new(
362 self.orig,
363 UnexpectedChar(ch),
364 )));
365 }
366 } else {
367 return Some(Err(ParseError::new(
369 self.orig,
370 IncompleteExpr("identifier"),
371 )));
372 }
373 } else {
374 (start, false)
377 }
378 } else {
379 (start, false)
381 };
382 while let Some(&(end, ch)) = self.s.peek() {
383 if !is_ident_rest(ch) {
384 return Some(Ok(Token::Ident(raw, &self.orig[start..end])));
385 } else {
386 self.s.next();
387 }
388 }
389 return Some(Ok(Token::Ident(raw, &self.orig[start..])));
390 }
391 Some((_, ch)) => {
392 return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch))));
393 }
394 None => return None,
395 }
396 }
397 }
398}
399
400fn is_ident_start(ch: char) -> bool {
401 ch == '_' || ch.is_ascii_alphabetic()
402}
403
404fn is_ident_rest(ch: char) -> bool {
405 is_ident_start(ch) || ch.is_ascii_digit()
406}
407
408impl<'a> Token<'a> {
409 fn classify(&self) -> &'static str {
410 match *self {
411 Token::LeftParen => "`(`",
412 Token::RightParen => "`)`",
413 Token::Ident(..) => "an identifier",
414 Token::Comma => "`,`",
415 Token::Equals => "`=`",
416 Token::String(..) => "a string",
417 }
418 }
419}