1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{
6 parse::{Parse, ParseStream, Result},
7 parse_macro_input, Expr, ExprArray, ExprLit, Ident, Lit, LitStr, Token,
8};
9
10struct ExclusiveFeatures {
11 name: Ident,
12 features: Vec<String>,
13 msg: LitStr,
14}
15
16impl Parse for ExclusiveFeatures {
17 fn parse(input: ParseStream) -> Result<Self> {
18 let name: Ident = input.parse()?;
19 input.parse::<Token![,]>()?;
20 let features: ExprArray = input.parse()?;
21 input.parse::<Token![,]>()?;
22 let msg: LitStr = input.parse()?;
23 Ok(ExclusiveFeatures {
24 name,
25 features: features
26 .elems
27 .iter()
28 .map(|e| {
29 if let Expr::Lit(ExprLit {
30 lit: Lit::Str(s), ..
31 }) = e
32 {
33 s.value()
34 } else {
35 panic!("Expected string literal")
36 }
37 })
38 .collect(),
39 msg,
40 })
41 }
42}
43
44#[proc_macro]
45pub fn cfg_exclusive(input: TokenStream) -> TokenStream {
46 let ExclusiveFeatures {
47 name,
48 features,
49 msg,
50 } = parse_macro_input!(input);
51
52 let mut permutations = vec![];
53
54 for f in features.iter() {
55 let mut cfg_exprs = vec![];
56
57 for ef in features.iter().filter(|&x| x != f) {
58 cfg_exprs.push(quote! {feature = #ef});
59 }
60
61 permutations.push(quote! {all(feature = #f, any(#(#cfg_exprs),*))});
62 }
63
64 TokenStream::from(quote! {
65 fn #name() {
66 #[cfg(any(#(#permutations),*))]
67 compile_error!(#msg);
68 }
69 })
70}