1mod cfg_expr;
4mod dnf;
5#[cfg(test)]
6mod tests;
7
8use std::fmt;
9
10use rustc_hash::FxHashSet;
11
12use intern::{Symbol, sym};
13
14pub use cfg_expr::{CfgAtom, CfgExpr};
15pub use dnf::DnfExpr;
16
17#[derive(Clone, PartialEq, Eq)]
28pub struct CfgOptions {
29 enabled: FxHashSet<CfgAtom>,
30}
31
32impl Default for CfgOptions {
33 fn default() -> Self {
34 Self { enabled: FxHashSet::from_iter([CfgAtom::Flag(sym::true_)]) }
35 }
36}
37
38impl fmt::Debug for CfgOptions {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 let mut items = self
41 .enabled
42 .iter()
43 .map(|atom| match atom {
44 CfgAtom::Flag(it) => it.to_string(),
45 CfgAtom::KeyValue { key, value } => format!("{key}={value}"),
46 })
47 .collect::<Vec<_>>();
48 items.sort();
49 f.debug_tuple("CfgOptions").field(&items).finish()
50 }
51}
52
53impl CfgOptions {
54 pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
55 cfg.fold(&|atom| self.enabled.contains(atom))
56 }
57
58 pub fn check_atom(&self, cfg: &CfgAtom) -> bool {
59 self.enabled.contains(cfg)
60 }
61
62 pub fn insert_atom(&mut self, key: Symbol) {
63 self.insert_any_atom(CfgAtom::Flag(key));
64 }
65
66 pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) {
67 self.insert_any_atom(CfgAtom::KeyValue { key, value });
68 }
69
70 pub fn apply_diff(&mut self, diff: CfgDiff) {
71 for atom in diff.enable {
72 self.insert_any_atom(atom);
73 }
74
75 for atom in diff.disable {
76 let (CfgAtom::Flag(sym) | CfgAtom::KeyValue { key: sym, .. }) = &atom;
77 if *sym == sym::true_ || *sym == sym::false_ {
78 tracing::error!("cannot remove `true` or `false` from cfg");
79 continue;
80 }
81 self.enabled.remove(&atom);
82 }
83 }
84
85 fn insert_any_atom(&mut self, atom: CfgAtom) {
86 let (CfgAtom::Flag(sym) | CfgAtom::KeyValue { key: sym, .. }) = &atom;
87 if *sym == sym::true_ || *sym == sym::false_ {
88 tracing::error!("cannot insert `true` or `false` to cfg");
89 return;
90 }
91 self.enabled.insert(atom);
92 }
93
94 pub fn get_cfg_keys(&self) -> impl Iterator<Item = &Symbol> {
95 self.enabled.iter().map(|it| match it {
96 CfgAtom::Flag(key) => key,
97 CfgAtom::KeyValue { key, .. } => key,
98 })
99 }
100
101 pub fn get_cfg_values<'a>(&'a self, cfg_key: &'a str) -> impl Iterator<Item = &'a Symbol> + 'a {
102 self.enabled.iter().filter_map(move |it| match it {
103 CfgAtom::KeyValue { key, value } if cfg_key == key.as_str() => Some(value),
104 _ => None,
105 })
106 }
107
108 pub fn to_hashable(&self) -> HashableCfgOptions {
109 let mut enabled = self.enabled.iter().cloned().collect::<Box<[_]>>();
110 enabled.sort_unstable();
111 HashableCfgOptions { _enabled: enabled }
112 }
113
114 #[inline]
115 pub fn shrink_to_fit(&mut self) {
116 self.enabled.shrink_to_fit();
117 }
118
119 pub fn append(&mut self, other: CfgOptions) {
120 self.enabled.extend(other.enabled);
124 }
125}
126
127impl Extend<CfgAtom> for CfgOptions {
128 fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
129 iter.into_iter().for_each(|cfg_flag| self.insert_any_atom(cfg_flag));
130 }
131}
132
133impl IntoIterator for CfgOptions {
134 type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;
135
136 type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
137
138 fn into_iter(self) -> Self::IntoIter {
139 <FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
140 }
141}
142
143impl<'a> IntoIterator for &'a CfgOptions {
144 type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;
145
146 type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
147
148 fn into_iter(self) -> Self::IntoIter {
149 <&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
150 }
151}
152
153impl FromIterator<CfgAtom> for CfgOptions {
154 fn from_iter<T: IntoIterator<Item = CfgAtom>>(iter: T) -> Self {
155 let mut options = CfgOptions::default();
156 options.extend(iter);
157 options
158 }
159}
160
161#[derive(Default, Clone, Debug, PartialEq, Eq)]
162pub struct CfgDiff {
163 enable: Vec<CfgAtom>,
165 disable: Vec<CfgAtom>,
166}
167
168impl CfgDiff {
169 pub fn new(mut enable: Vec<CfgAtom>, mut disable: Vec<CfgAtom>) -> CfgDiff {
171 enable.sort();
172 enable.dedup();
173 disable.sort();
174 disable.dedup();
175 for i in (0..enable.len()).rev() {
176 if let Some(j) = disable.iter().position(|atom| *atom == enable[i]) {
177 enable.remove(i);
178 disable.remove(j);
179 }
180 }
181
182 CfgDiff { enable, disable }
183 }
184
185 pub fn len(&self) -> usize {
187 self.enable.len() + self.disable.len()
188 }
189
190 pub fn is_empty(&self) -> bool {
191 self.len() == 0
192 }
193}
194
195impl fmt::Display for CfgDiff {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 if !self.enable.is_empty() {
198 f.write_str("enable ")?;
199 for (i, atom) in self.enable.iter().enumerate() {
200 let sep = match i {
201 0 => "",
202 _ if i == self.enable.len() - 1 => " and ",
203 _ => ", ",
204 };
205 f.write_str(sep)?;
206
207 atom.fmt(f)?;
208 }
209
210 if !self.disable.is_empty() {
211 f.write_str("; ")?;
212 }
213 }
214
215 if !self.disable.is_empty() {
216 f.write_str("disable ")?;
217 for (i, atom) in self.disable.iter().enumerate() {
218 let sep = match i {
219 0 => "",
220 _ if i == self.enable.len() - 1 => " and ",
221 _ => ", ",
222 };
223 f.write_str(sep)?;
224
225 atom.fmt(f)?;
226 }
227 }
228
229 Ok(())
230 }
231}
232
233pub struct InactiveReason {
234 enabled: Vec<CfgAtom>,
235 disabled: Vec<CfgAtom>,
236}
237
238impl fmt::Display for InactiveReason {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 if !self.enabled.is_empty() {
241 for (i, atom) in self.enabled.iter().enumerate() {
242 let sep = match i {
243 0 => "",
244 _ if i == self.enabled.len() - 1 => " and ",
245 _ => ", ",
246 };
247 f.write_str(sep)?;
248
249 atom.fmt(f)?;
250 }
251 let is_are = if self.enabled.len() == 1 { "is" } else { "are" };
252 write!(f, " {is_are} enabled")?;
253
254 if !self.disabled.is_empty() {
255 f.write_str(" and ")?;
256 }
257 }
258
259 if !self.disabled.is_empty() {
260 for (i, atom) in self.disabled.iter().enumerate() {
261 let sep = match i {
262 0 => "",
263 _ if i == self.disabled.len() - 1 => " and ",
264 _ => ", ",
265 };
266 f.write_str(sep)?;
267
268 atom.fmt(f)?;
269 }
270 let is_are = if self.disabled.len() == 1 { "is" } else { "are" };
271 write!(f, " {is_are} disabled")?;
272 }
273
274 Ok(())
275 }
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Hash)]
280pub struct HashableCfgOptions {
281 _enabled: Box<[CfgAtom]>,
282}