conflicting/lib.rs
1use proc_macro::TokenStream;
2
3mod input;
4
5/// The conflicting macro lets you pass a set of feature gated expressions that transitively conflict with
6/// each other. (ie. a feature conflicts with the powerset of all other features in the block)
7///
8/// You may have expressions gated by features a, b, and c, but theyre all mutually
9/// exclusive. To handle this macro will throw an error at compile time.
10///
11/// # Example
12///
13/// ```ignore
14/// fn new() -> &'static str {
15/// conflicting! {
16/// a => {
17/// "a"
18/// },
19/// b => {
20/// "b"
21/// },
22/// c => {
23/// "c"
24/// },
25/// }
26/// }
27/// ```
28///
29/// This will generate something like:
30/// ```ignore
31/// fn new() -> &'static str {
32/// #[cfg(feature = "a")]
33/// {
34/// "a"
35///
36/// #[cfg(feature = "b")]
37/// compile_error!("a conflicts with b");
38///
39/// #[cfg(feature = "c")]
40/// compile_error!("a conflicts with c");
41/// }
42///
43/// #[cfg(feature = "b")]
44/// {
45/// "b"
46
47/// #[cfg(feature = "a")]
48/// compile_error!("b conflicts with a");
49///
50/// #[cfg(feature = "c")]
51/// compile_error!("b conflicts with c");
52/// }
53///
54/// #[cfg(feature = "c")]
55/// {
56/// "c"
57///
58/// #[cfg(feature = "a")]
59/// compile_error!("c conflicts with a");
60///
61/// #[cfg(feature = "b")]
62/// compile_error!("c conflicts with b");
63/// }
64/// }
65/// ```
66#[proc_macro]
67pub fn conflicting(input: TokenStream) -> TokenStream {
68 let input = syn::parse_macro_input!(input as input::ConflictingInput);
69
70 let out: proc_macro2::TokenStream = input.0.iter().map(|gated_expr| {
71 let feature = &gated_expr.feature;
72 let expr = &gated_expr.expr;
73
74 let conflicting_cases = input.0.iter()
75 .filter(|other| other.feature != *feature)
76 .map(|other| {
77 let other_feature = &other.feature;
78 quote::quote! {
79 #[cfg(feature = #other_feature)]
80 {
81 compile_error!(concat!(stringify!(#feature), " conflicts with ", stringify!(#other_feature)));
82 }
83 }
84 });
85
86 quote::quote! {
87 #[cfg(feature = #feature)]
88 {
89 #(#conflicting_cases)*
90
91 #expr
92 }
93 }
94 })
95 .collect();
96
97 out.into()
98}