1use indexmap::{indexset as set, IndexSet as Set};
2use proc_macro2::Ident;
3use std::hash::{Hash, Hasher};
4use std::iter;
5use std::mem;
6use syn::parse::{Error, ParseStream, Result};
7use syn::{parenthesized, token, Attribute, LitStr, Token};
8
9#[derive(Clone)]
10pub(crate) enum CfgExpr {
11 Unconditional,
12 Eq(Ident, Option<LitStr>),
13 All(Vec<CfgExpr>),
14 Any(Vec<CfgExpr>),
15 Not(Box<CfgExpr>),
16}
17
18#[derive(Clone)]
19pub(crate) enum ComputedCfg<'a> {
20 Leaf(&'a CfgExpr),
21 All(Set<&'a CfgExpr>),
22 Any(Set<ComputedCfg<'a>>),
23}
24
25impl CfgExpr {
26 pub(crate) fn merge_and(&mut self, expr: CfgExpr) {
27 if let CfgExpr::Unconditional = self {
28 *self = expr;
29 } else if let CfgExpr::Unconditional = expr {
30 } else if let CfgExpr::All(list) = self {
32 list.push(expr);
33 } else {
34 let prev = mem::replace(self, CfgExpr::Unconditional);
35 *self = CfgExpr::All(vec![prev, expr]);
36 }
37 }
38}
39
40impl<'a> ComputedCfg<'a> {
41 pub(crate) fn all(one: &'a CfgExpr, two: &'a CfgExpr) -> Self {
42 if let (cfg, CfgExpr::Unconditional) | (CfgExpr::Unconditional, cfg) = (one, two) {
43 ComputedCfg::Leaf(cfg)
44 } else if one == two {
45 ComputedCfg::Leaf(one)
46 } else {
47 ComputedCfg::All(set![one, two])
48 }
49 }
50
51 pub(crate) fn merge_or(&mut self, other: impl Into<ComputedCfg<'a>>) {
52 let other = other.into();
53 if let ComputedCfg::Leaf(CfgExpr::Unconditional) = self {
54 } else if let ComputedCfg::Leaf(CfgExpr::Unconditional) = other {
56 *self = other;
57 } else if *self == other {
58 } else if let ComputedCfg::Any(list) = self {
60 list.insert(other);
61 } else {
62 let prev = mem::replace(self, ComputedCfg::Any(Set::new()));
63 let ComputedCfg::Any(list) = self else {
64 unreachable!();
65 };
66 list.extend([prev, other]);
67 }
68 }
69}
70
71impl<'a> From<&'a CfgExpr> for ComputedCfg<'a> {
72 fn from(cfg: &'a CfgExpr) -> Self {
73 ComputedCfg::Leaf(cfg)
74 }
75}
76
77impl Eq for CfgExpr {}
78
79impl PartialEq for CfgExpr {
80 fn eq(&self, other: &Self) -> bool {
81 match (self, other) {
82 (CfgExpr::Unconditional, CfgExpr::Unconditional) => true,
83 (CfgExpr::Eq(this_ident, None), CfgExpr::Eq(other_ident, None)) => {
84 this_ident == other_ident
85 }
86 (
87 CfgExpr::Eq(this_ident, Some(this_value)),
88 CfgExpr::Eq(other_ident, Some(other_value)),
89 ) => {
90 this_ident == other_ident
91 && this_value.token().to_string() == other_value.token().to_string()
92 }
93 (CfgExpr::All(this), CfgExpr::All(other))
94 | (CfgExpr::Any(this), CfgExpr::Any(other)) => this == other,
95 (CfgExpr::Not(this), CfgExpr::Not(other)) => this == other,
96 (_, _) => false,
97 }
98 }
99}
100
101impl Hash for CfgExpr {
102 fn hash<H: Hasher>(&self, hasher: &mut H) {
103 mem::discriminant(self).hash(hasher);
104 match self {
105 CfgExpr::Unconditional => {}
106 CfgExpr::Eq(ident, value) => {
107 ident.hash(hasher);
108 value.as_ref().map(LitStr::value).hash(hasher);
110 }
111 CfgExpr::All(inner) | CfgExpr::Any(inner) => inner.hash(hasher),
112 CfgExpr::Not(inner) => inner.hash(hasher),
113 }
114 }
115}
116
117impl<'a> Eq for ComputedCfg<'a> {}
118
119impl<'a> PartialEq for ComputedCfg<'a> {
120 fn eq(&self, other: &Self) -> bool {
121 match (self, other) {
122 (ComputedCfg::Leaf(this), ComputedCfg::Leaf(other)) => this == other,
123 (ComputedCfg::All(this), ComputedCfg::All(other)) => {
127 this.len() == other.len()
128 && iter::zip(this, other).all(|(this, other)| this == other)
129 }
130 (ComputedCfg::Any(this), ComputedCfg::Any(other)) => {
131 this.len() == other.len()
132 && iter::zip(this, other).all(|(this, other)| this == other)
133 }
134 (_, _) => false,
135 }
136 }
137}
138
139impl<'a> Hash for ComputedCfg<'a> {
140 fn hash<H: Hasher>(&self, hasher: &mut H) {
141 mem::discriminant(self).hash(hasher);
142 match self {
143 ComputedCfg::Leaf(cfg) => cfg.hash(hasher),
144 ComputedCfg::All(inner) => inner.iter().for_each(|cfg| cfg.hash(hasher)),
145 ComputedCfg::Any(inner) => inner.iter().for_each(|cfg| cfg.hash(hasher)),
146 }
147 }
148}
149
150pub(crate) fn parse_attribute(attr: &Attribute) -> Result<CfgExpr> {
151 attr.parse_args_with(|input: ParseStream| {
152 let cfg_expr = input.call(parse_single)?;
153 input.parse::<Option<Token![,]>>()?;
154 Ok(cfg_expr)
155 })
156}
157
158fn parse_single(input: ParseStream) -> Result<CfgExpr> {
159 let ident: Ident = input.parse()?;
160 let lookahead = input.lookahead1();
161 if input.peek(token::Paren) {
162 let content;
163 parenthesized!(content in input);
164 if ident == "all" {
165 let list = content.call(parse_multiple)?;
166 Ok(CfgExpr::All(list))
167 } else if ident == "any" {
168 let list = content.call(parse_multiple)?;
169 Ok(CfgExpr::Any(list))
170 } else if ident == "not" {
171 let expr = content.call(parse_single)?;
172 content.parse::<Option<Token![,]>>()?;
173 Ok(CfgExpr::Not(Box::new(expr)))
174 } else {
175 Err(Error::new(ident.span(), "unrecognized cfg expression"))
176 }
177 } else if lookahead.peek(Token![=]) {
178 input.parse::<Token![=]>()?;
179 let string: LitStr = input.parse()?;
180 Ok(CfgExpr::Eq(ident, Some(string)))
181 } else if lookahead.peek(Token![,]) || input.is_empty() {
182 Ok(CfgExpr::Eq(ident, None))
183 } else {
184 Err(lookahead.error())
185 }
186}
187
188fn parse_multiple(input: ParseStream) -> Result<Vec<CfgExpr>> {
189 let mut vec = Vec::new();
190 while !input.is_empty() {
191 let expr = input.call(parse_single)?;
192 vec.push(expr);
193 if input.is_empty() {
194 break;
195 }
196 input.parse::<Token![,]>()?;
197 }
198 Ok(vec)
199}