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 #[cfg(feature = "tt")]
72 pub fn parse_from_iter<S: Copy>(tt: &mut tt::iter::TtIter<'_, S>) -> CfgExpr {
73 next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid)
74 }
75
76 pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
78 match self {
79 CfgExpr::Invalid => None,
80 CfgExpr::Atom(atom) => Some(query(atom)),
81 CfgExpr::All(preds) => {
82 preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?))
83 }
84 CfgExpr::Any(preds) => {
85 preds.iter().try_fold(false, |s, pred| Some(s || pred.fold(query)?))
86 }
87 CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
88 }
89 }
90}
91
92#[cfg(feature = "tt")]
93fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
94 use intern::sym;
95 use tt::iter::TtElement;
96
97 let name = match it.next() {
98 None => return None,
99 Some(TtElement::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
100 Some(_) => return Some(CfgExpr::Invalid),
101 };
102
103 let ret = match it.peek() {
104 Some(TtElement::Leaf(tt::Leaf::Punct(punct)))
105 if punct.char == '='
107 && (punct.spacing == tt::Spacing::Alone
108 || it.remaining().flat_tokens().get(1).is_none_or(|peek2| {
109 !matches!(peek2, tt::TokenTree::Leaf(tt::Leaf::Punct(_)))
110 })) =>
111 {
112 match it.remaining().flat_tokens().get(1) {
113 Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
114 it.next();
115 it.next();
116 CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into()
117 }
118 _ => return Some(CfgExpr::Invalid),
119 }
120 }
121 Some(TtElement::Subtree(_, mut sub_it)) => {
122 it.next();
123 let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it));
124 match name {
125 s if s == sym::all => CfgExpr::All(subs.collect()),
126 s if s == sym::any => CfgExpr::Any(subs.collect()),
127 s if s == sym::not => {
128 CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
129 }
130 _ => CfgExpr::Invalid,
131 }
132 }
133 _ => CfgAtom::Flag(name).into(),
134 };
135
136 if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = it.peek()
138 && punct.char == ','
139 {
140 it.next();
141 }
142 Some(ret)
143}
144
145#[cfg(test)]
146impl arbitrary::Arbitrary<'_> for CfgAtom {
147 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
148 if u.arbitrary()? {
149 Ok(CfgAtom::Flag(Symbol::intern(<_>::arbitrary(u)?)))
150 } else {
151 Ok(CfgAtom::KeyValue {
152 key: Symbol::intern(<_>::arbitrary(u)?),
153 value: Symbol::intern(<_>::arbitrary(u)?),
154 })
155 }
156 }
157}