1 use proc_macro2::Ident;
2 use std::mem;
3 use syn::parse::{Error, ParseStream, Result};
4 use syn::{parenthesized, token, Attribute, LitStr, Token};
5 
6 #[derive(Clone)]
7 pub(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 
18 impl CfgExpr {
merge(&mut self, expr: 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 
parse_attribute(attr: &Attribute) -> Result<CfgExpr>31 pub(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 
parse_single(input: ParseStream) -> Result<CfgExpr>39 fn 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 
parse_multiple(input: ParseStream) -> Result<Vec<CfgExpr>>69 fn 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 }
81