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)] Eq(Ident, Option<LitStr>),
11 All(Vec<CfgExpr>),
12 #[allow(dead_code)] Any(Vec<CfgExpr>),
14 #[allow(dead_code)] 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}