cxx_gen/syntax/
cfg.rs

1use proc_macro2::Ident;
2use std::mem;
3use syn::parse::{Error, ParseStream, Result};
4use syn::{parenthesized, token, Attribute, LitStr, Token};
5
6#[derive(Clone)]
7pub(crate) enum CfgExpr {
8    Unconditional,
9    #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
10    Eq(Ident, Option<LitStr>),
11    All(Vec<CfgExpr>),
12    #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
13    Any(Vec<CfgExpr>),
14    #[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
15    Not(Box<CfgExpr>),
16}
17
18impl CfgExpr {
19    pub(crate) fn merge(&mut self, expr: CfgExpr) {
20        if let CfgExpr::Unconditional = self {
21            *self = expr;
22        } else if let CfgExpr::All(list) = self {
23            list.push(expr);
24        } else {
25            let prev = mem::replace(self, CfgExpr::Unconditional);
26            *self = CfgExpr::All(vec![prev, expr]);
27        }
28    }
29}
30
31pub(crate) fn parse_attribute(attr: &Attribute) -> Result<CfgExpr> {
32    attr.parse_args_with(|input: ParseStream| {
33        let cfg_expr = input.call(parse_single)?;
34        input.parse::<Option<Token![,]>>()?;
35        Ok(cfg_expr)
36    })
37}
38
39fn parse_single(input: ParseStream) -> Result<CfgExpr> {
40    let ident: Ident = input.parse()?;
41    let lookahead = input.lookahead1();
42    if input.peek(token::Paren) {
43        let content;
44        parenthesized!(content in input);
45        if ident == "all" {
46            let list = content.call(parse_multiple)?;
47            Ok(CfgExpr::All(list))
48        } else if ident == "any" {
49            let list = content.call(parse_multiple)?;
50            Ok(CfgExpr::Any(list))
51        } else if ident == "not" {
52            let expr = content.call(parse_single)?;
53            content.parse::<Option<Token![,]>>()?;
54            Ok(CfgExpr::Not(Box::new(expr)))
55        } else {
56            Err(Error::new(ident.span(), "unrecognized cfg expression"))
57        }
58    } else if lookahead.peek(Token![=]) {
59        input.parse::<Token![=]>()?;
60        let string: LitStr = input.parse()?;
61        Ok(CfgExpr::Eq(ident, Some(string)))
62    } else if lookahead.peek(Token![,]) || input.is_empty() {
63        Ok(CfgExpr::Eq(ident, None))
64    } else {
65        Err(lookahead.error())
66    }
67}
68
69fn parse_multiple(input: ParseStream) -> Result<Vec<CfgExpr>> {
70    let mut vec = Vec::new();
71    while !input.is_empty() {
72        let expr = input.call(parse_single)?;
73        vec.push(expr);
74        if input.is_empty() {
75            break;
76        }
77        input.parse::<Token![,]>()?;
78    }
79    Ok(vec)
80}