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