1#![doc = include_str!("../README.md")]
2
3mod cfg;
4use cfg::*;
5mod find;
6use find::*;
7
8use std::{collections::HashSet, iter};
9
10use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
11use quote::ToTokens;
12use syn::{
13 Item, Stmt,
14 parse::{Parse, ParseStream},
15};
16
17struct Many<T>(Vec<T>);
18
19impl<T: Parse> Parse for Many<T> {
20 fn parse(input: ParseStream) -> syn::Result<Self> {
21 let mut items = Vec::new();
22 while !input.is_empty() {
23 items.push(input.parse::<T>()?);
24 }
25 Ok(Many(items))
26 }
27}
28
29fn expand_for_cfg(ts: TokenStream, active_cfg: &Cfg) -> TokenStream {
30 let mut it = ts.into_iter().peekable();
31 let mut out = TokenStream::new();
32 while let Some(tt) = it.next() {
33 match &tt {
34 TokenTree::Group(g) => {
35 let expanded = expand_for_cfg(g.stream(), active_cfg);
36 let expanded = TokenTree::Group(Group::new(g.delimiter(), expanded));
37 out.extend([expanded]);
38 }
39 TokenTree::Punct(p) if p.as_char() == '#' => {
40 let Some(TokenTree::Group(g)) = it.peek() else {
42 out.extend([tt]);
43 continue;
44 };
45 if g.delimiter() != Delimiter::Bracket {
46 out.extend([tt]);
47 continue;
48 }
49
50 let mut attr_ts = TokenStream::new();
52 attr_ts.extend(iter::once(tt.clone()));
53 attr_ts.extend(iter::once(TokenTree::Group(g.clone())));
54
55 let Ok(attr) = parse_any_attr(attr_ts) else {
56 out.extend([tt]);
57 continue;
58 };
59 let Some(cfg) = Cfg::from_attr(&attr) else {
60 out.extend([tt]);
61 continue;
62 };
63 if !attr.path().is_ident("cfg") {
64 out.extend([tt]);
65 continue;
66 }
67
68 let _ = it.next();
70
71 let Some(target) = it.next() else { continue };
73 if active_cfg.implies(&cfg) {
74 let target = if let TokenTree::Group(g) = target {
76 g.stream()
77 } else {
78 target.into_token_stream()
79 };
80 let expanded = expand_for_cfg(target, active_cfg);
81 out.extend([expanded]);
82 } else {
83 }
85 }
86 _ => {
87 out.extend([tt]);
88 }
89 }
90 }
91
92 out
93}
94
95fn generate_all_combinations(cfgs: Vec<Cfg>) -> Vec<Cfg> {
96 fn core<T: Clone>(
97 items: &[T],
98 i: usize,
99 acc: &mut Vec<(T, bool)>,
100 out: &mut impl FnMut(&Vec<(T, bool)>),
101 ) {
102 if i == items.len() {
103 out(acc);
104 return;
105 }
106
107 acc.push((items[i].clone(), false));
109 core(items, i + 1, acc, out);
110 acc.pop();
111
112 acc.push((items[i].clone(), true));
114 core(items, i + 1, acc, out);
115 acc.pop();
116 }
117
118 if cfgs.is_empty() {
119 return Vec::new();
120 }
121
122 let mut acc = Vec::with_capacity(cfgs.len());
123 let mut out = Vec::with_capacity(cfgs.len() * cfgs.len());
124 core(&cfgs, 0, &mut acc, &mut |cfgs| {
125 let mut list = cfgs
126 .iter()
127 .cloned()
128 .map(
129 |(cfg, active)| {
130 if active { cfg } else { Cfg::Not(Box::new(cfg)) }
131 },
132 )
133 .collect::<Vec<_>>();
134 out.push(if list.len() == 1 {
135 list.pop().unwrap()
136 } else {
137 Cfg::All(list)
138 });
139 });
140 out
141}
142
143fn find_base_cfgs(input: impl IntoIterator<Item = Cfg>) -> Vec<Cfg> {
144 let mut cfgs = HashSet::new();
145
146 for cfg in input.into_iter() {
148 match cfg {
149 Cfg::Not(inner) => cfgs.insert(*inner),
150 Cfg::All(list) | Cfg::Any(list) if list.is_empty() => false,
151 Cfg::All(list) | Cfg::Any(list) if list.len() == 1 => cfgs.insert(list[0].clone()),
152 _ => cfgs.insert(cfg),
153 };
154 }
155
156 let cfgs: Vec<Cfg> = cfgs
158 .iter()
159 .filter(|cfg| match cfg {
160 Cfg::All(xs) => !xs.iter().all(|child| match child {
161 Cfg::Not(inner) => cfgs.contains(inner),
162 _ => cfgs.contains(child),
163 }),
164 _ => true,
165 })
166 .cloned()
167 .collect();
168
169 cfgs
170}
171
172#[proc_macro]
188pub fn cfg_tt(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
189 let content: TokenStream = input.into();
190
191 let cfgs = find_cfg_attrs(content.clone());
193 if cfgs.is_empty() {
194 return content.into();
196 }
197
198 let cfgs = find_base_cfgs(cfgs);
199
200 let configurations = generate_all_combinations(cfgs);
202
203 let mut out = TokenStream::new();
204 for cfg in &configurations {
205 let expanded = expand_for_cfg(content.clone(), cfg);
206 let items = match syn::parse2::<Many<Item>>(expanded.clone()) {
207 Ok(items) => items.0.iter().map(|item| item.to_token_stream()).collect(),
208 Err(_) => match syn::parse2::<Many<Stmt>>(expanded.clone()) {
209 Ok(stmts) => stmts.0.iter().map(|item| item.to_token_stream()).collect(),
210 Err(_) => vec![expanded],
211 },
212 };
213
214 for item in items {
215 out.extend([cfg.to_token_stream(), item]);
216 }
217 }
218
219 out.into()
220}