1use std::fmt;
6
7use intern::Symbol;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub enum CfgAtom {
12 Flag(Symbol),
14 KeyValue { key: Symbol, value: Symbol },
19}
20
21impl PartialOrd for CfgAtom {
22 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
23 Some(self.cmp(other))
24 }
25}
26
27impl Ord for CfgAtom {
28 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
29 match (self, other) {
30 (CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
31 (CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
32 (CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
33 (CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
34 key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
35 }
36 }
37 }
38}
39
40impl fmt::Display for CfgAtom {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 CfgAtom::Flag(name) => name.fmt(f),
44 CfgAtom::KeyValue { key, value } => write!(f, "{key} = {value:?}"),
45 }
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50#[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
51pub enum CfgExpr {
52 Invalid,
53 Atom(CfgAtom),
54 All(Box<[CfgExpr]>),
55 Any(Box<[CfgExpr]>),
56 Not(Box<CfgExpr>),
57}
58
59impl From<CfgAtom> for CfgExpr {
60 fn from(atom: CfgAtom) -> Self {
61 CfgExpr::Atom(atom)
62 }
63}
64
65impl CfgExpr {
66 #[cfg(feature = "tt")]
67 pub fn parse<S: Copy>(tt: &tt::TopSubtree<S>) -> CfgExpr {
68 next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
69 }
70
71 pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
73 match self {
74 CfgExpr::Invalid => None,
75 CfgExpr::Atom(atom) => Some(query(atom)),
76 CfgExpr::All(preds) => {
77 preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?))
78 }
79 CfgExpr::Any(preds) => {
80 preds.iter().try_fold(false, |s, pred| Some(s || pred.fold(query)?))
81 }
82 CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
83 }
84 }
85}
86
87#[cfg(feature = "tt")]
88fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
89 use intern::sym;
90 use tt::iter::TtElement;
91
92 let name = match it.next() {
93 None => return None,
94 Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
95 Some(_) => return Some(CfgExpr::Invalid),
96 };
97
98 let ret = match it.peek() {
99 Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
100 match it.remaining().flat_tokens().get(1) {
101 Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
102 it.next();
103 it.next();
104 CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into()
105 }
106 _ => return Some(CfgExpr::Invalid),
107 }
108 }
109 Some(TtElement::Subtree(_, mut sub_it)) => {
110 it.next();
111 let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it));
112 match name {
113 s if s == sym::all => CfgExpr::All(subs.collect()),
114 s if s == sym::any => CfgExpr::Any(subs.collect()),
115 s if s == sym::not => {
116 CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
117 }
118 _ => CfgExpr::Invalid,
119 }
120 }
121 _ => CfgAtom::Flag(name).into(),
122 };
123
124 if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = it.peek() {
126 if punct.char == ',' {
127 it.next();
128 }
129 }
130 Some(ret)
131}
132
133#[cfg(test)]
134impl arbitrary::Arbitrary<'_> for CfgAtom {
135 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
136 if u.arbitrary()? {
137 Ok(CfgAtom::Flag(Symbol::intern(<_>::arbitrary(u)?)))
138 } else {
139 Ok(CfgAtom::KeyValue {
140 key: Symbol::intern(<_>::arbitrary(u)?),
141 value: Symbol::intern(<_>::arbitrary(u)?),
142 })
143 }
144 }
145}