Skip to main content

cargo_platform/
cfg.rs

1use crate::error::{ParseError, ParseErrorKind::*};
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::iter;
5use std::str::{self, FromStr};
6
7/// A cfg expression.
8#[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/// A cfg value.
19#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)]
20pub enum Cfg {
21    /// A named cfg value, like `unix`.
22    Name(Ident),
23    /// A key/value cfg pair, like `target_os = "linux"`.
24    KeyPair(Ident, String),
25}
26
27/// A identifier
28#[derive(Eq, Ord, PartialOrd, Clone, Debug)]
29pub struct Ident {
30    /// The identifier
31    pub name: String,
32    /// Is this a raw ident: `r#async`
33    ///
34    /// It's mainly used for display and doesn't take
35    /// part in the hash or equality (`foo` == `r#foo`).
36    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
49/// The list of keywords.
50///
51/// We should consider all the keywords, but some are conditional on
52/// the edition so for now we just consider true/false.
53///
54/// <https://doc.rust-lang.org/reference/keywords.html>
55pub(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    /// Utility function to check if the key, "cfg(..)" matches the `target_cfg`
153    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    /// Returns the rest of the input from the current location.
370    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                            // starts with `r#` is a raw ident
405                            self.s.next();
406                            if let Some((start, ch)) = self.s.next() {
407                                if is_ident_start(ch) {
408                                    (start, true)
409                                } else {
410                                    // not a starting ident character
411                                    return Some(Err(ParseError::new(
412                                        self.orig,
413                                        UnexpectedChar(ch),
414                                    )));
415                                }
416                            } else {
417                                // not followed by a ident, error out
418                                return Some(Err(ParseError::new(
419                                    self.orig,
420                                    IncompleteExpr("identifier"),
421                                )));
422                            }
423                        } else {
424                            // starts with `r` but not does continue with `#`
425                            // cannot be a raw ident
426                            (start, false)
427                        }
428                    } else {
429                        // do not start with `r`, cannot be a raw ident
430                        (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}