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
120impl Extend<CfgAtom> for CfgOptions {
121 fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
122 iter.into_iter().for_each(|cfg_flag| self.insert_any_atom(cfg_flag));
123 }
124}
125
126impl IntoIterator for CfgOptions {
127 type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;
128
129 type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
130
131 fn into_iter(self) -> Self::IntoIter {
132 <FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
133 }
134}
135
136impl<'a> IntoIterator for &'a CfgOptions {
137 type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;
138
139 type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
140
141 fn into_iter(self) -> Self::IntoIter {
142 <&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
143 }
144}
145
146impl FromIterator<CfgAtom> for CfgOptions {
147 fn from_iter<T: IntoIterator<Item = CfgAtom>>(iter: T) -> Self {
148 let mut options = CfgOptions::default();
149 options.extend(iter);
150 options
151 }
152}
153
154#[derive(Default, Clone, Debug, PartialEq, Eq)]
155pub struct CfgDiff {
156 enable: Vec<CfgAtom>,
158 disable: Vec<CfgAtom>,
159}
160
161impl CfgDiff {
162 pub fn new(mut enable: Vec<CfgAtom>, mut disable: Vec<CfgAtom>) -> CfgDiff {
164 enable.sort();
165 enable.dedup();
166 disable.sort();
167 disable.dedup();
168 for i in (0..enable.len()).rev() {
169 if let Some(j) = disable.iter().position(|atom| *atom == enable[i]) {
170 enable.remove(i);
171 disable.remove(j);
172 }
173 }
174
175 CfgDiff { enable, disable }
176 }
177
178 pub fn len(&self) -> usize {
180 self.enable.len() + self.disable.len()
181 }
182
183 pub fn is_empty(&self) -> bool {
184 self.len() == 0
185 }
186}
187
188impl fmt::Display for CfgDiff {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 if !self.enable.is_empty() {
191 f.write_str("enable ")?;
192 for (i, atom) in self.enable.iter().enumerate() {
193 let sep = match i {
194 0 => "",
195 _ if i == self.enable.len() - 1 => " and ",
196 _ => ", ",
197 };
198 f.write_str(sep)?;
199
200 atom.fmt(f)?;
201 }
202
203 if !self.disable.is_empty() {
204 f.write_str("; ")?;
205 }
206 }
207
208 if !self.disable.is_empty() {
209 f.write_str("disable ")?;
210 for (i, atom) in self.disable.iter().enumerate() {
211 let sep = match i {
212 0 => "",
213 _ if i == self.enable.len() - 1 => " and ",
214 _ => ", ",
215 };
216 f.write_str(sep)?;
217
218 atom.fmt(f)?;
219 }
220 }
221
222 Ok(())
223 }
224}
225
226pub struct InactiveReason {
227 enabled: Vec<CfgAtom>,
228 disabled: Vec<CfgAtom>,
229}
230
231impl fmt::Display for InactiveReason {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 if !self.enabled.is_empty() {
234 for (i, atom) in self.enabled.iter().enumerate() {
235 let sep = match i {
236 0 => "",
237 _ if i == self.enabled.len() - 1 => " and ",
238 _ => ", ",
239 };
240 f.write_str(sep)?;
241
242 atom.fmt(f)?;
243 }
244 let is_are = if self.enabled.len() == 1 { "is" } else { "are" };
245 write!(f, " {is_are} enabled")?;
246
247 if !self.disabled.is_empty() {
248 f.write_str(" and ")?;
249 }
250 }
251
252 if !self.disabled.is_empty() {
253 for (i, atom) in self.disabled.iter().enumerate() {
254 let sep = match i {
255 0 => "",
256 _ if i == self.disabled.len() - 1 => " and ",
257 _ => ", ",
258 };
259 f.write_str(sep)?;
260
261 atom.fmt(f)?;
262 }
263 let is_are = if self.disabled.len() == 1 { "is" } else { "are" };
264 write!(f, " {is_are} disabled")?;
265 }
266
267 Ok(())
268 }
269}
270
271#[derive(Debug, Clone, PartialEq, Eq, Hash)]
273pub struct HashableCfgOptions {
274 _enabled: Box<[CfgAtom]>,
275}