1 //! A convenience macro to ergonomically define an item depending on a large
2 //! number of `#[cfg]` parameters. Structured like match statement, the first
3 //! matching branch is the item that gets emitted.
4 
5 #![cfg_attr(not(feature = "use_core"), feature(no_core))]
6 #![doc(html_root_url = "https://docs.rs/cfg-if")]
7 #![cfg_attr(test, deny(warnings))]
8 #![cfg_attr(not(feature = "use_core"), no_core)]
9 #![cfg_attr(feature = "use_core", no_std)]
10 
11 /// The macro provided by this crate, `match_cfg`, is similar to the `if/elif` C
12 /// preprocessor directives and allows defining a cascade of `#[cfg]` cases,
13 /// emitting the implementation which matches first.
14 ///
15 /// This conveniently allows providing a long list `#[cfg]`'d blocks of code
16 /// without having to rewrite each `cfg()` clause multiple times.
17 ///
18 /// # Example
19 ///
20 /// ```
21 /// #[macro_use(match_cfg)]
22 /// extern crate match_cfg;
23 ///
24 /// match_cfg! {
25 ///     #[cfg(unix)] => {
26 ///         fn foo() { /* unix specific functionality */ }
27 ///     }
28 ///     #[cfg(target_pointer_width = "32")] => {
29 ///         fn foo() { /* non-unix, 32-bit functionality */ }
30 ///     }
31 ///     _ => {
32 ///         fn foo() { /* fallback implementation */ }
33 ///     }
34 /// }
35 /// # fn main() {}
36 /// ```
37 #[macro_export]
38 macro_rules! match_cfg {
39     (#[cfg($cfg:meta)] => { $($i:item)* }) => {
40         $(
41             #[cfg($cfg)] $i
42         )*
43     };
44     (#[cfg($cfg:meta)] @ #[cfg($cfg_not:meta)] => { $($i:item)* }) => {
45         $(
46             #[cfg(not($cfg_not))] #[cfg($cfg)] $i
47         )*
48     };
49     (_ => { $($i:item)* }) => { $( $i )* };
50     (_ @ #[cfg($cfg_not:meta)] => { $($i:item)* }) => {
51         $(
52             #[cfg(not($cfg_not))] $i
53         )*
54     };
55     (
56         #[cfg($cfg0:meta)] => { $($i:item)* }
57         $(#[cfg($cfgs:meta)] => { $($is:item)* })*
58     ) => {
59         match_cfg! {
60             #[cfg($cfg0)] => { $($i)* }
61         }
62         $(
63             match_cfg! {
64                 #[cfg($cfgs)] @ #[cfg($cfg0)] => { $($is)* }
65             }
66         )*
67     };
68     (
69         $(#[cfg($cfgs:meta)] => { $($is:item)* })*
70         _ => { $($ni:item)* }
71     ) => {
72         match_cfg! {
73             $( #[cfg($cfgs)] => { $($is)* } )*
74         }
75         match_cfg! {
76             _ @ #[cfg(any($($cfgs),*))] => { $($ni)* }
77         }
78     };
79 }
80 
81 #[cfg(test)]
82 mod tests {
83     match_cfg! {
84         #[cfg(target_pointer_width = "64")] => { fn f0_() -> bool { true }}
85     }
86     match_cfg! {
87         #[cfg(unix)] => { fn f1_() -> bool { true }}
88         #[cfg(any(target_os = "macos", target_os = "linux"))] => { fn f1_() -> bool { false }}
89     }
90 
91     match_cfg! {
92         #[cfg(target_pointer_width = "64")] => { fn f2_() -> bool { true }}
93         #[cfg(target_pointer_width = "32")] => { fn f2_() -> bool { false }}
94     }
95 
96     match_cfg! {
97         #[cfg(target_pointer_width = "8")] => { fn f3_() -> i32 { 0 }}
98         #[cfg(target_pointer_width = "16")] => { fn f3_() -> i32 { 1 }}
99         _ => { fn f3_() -> i32 { 2 }}
100     }
101 
102     #[test]
tests()103     fn tests() {
104         #[cfg(target_pointer_width = "64")]
105         {
106             assert!(f0_());
107         }
108         #[cfg(unix)]
109         {
110             assert!(f1_());
111         }
112         assert!(f2_());
113         assert_eq!(f3_(), 2);
114     }
115 
116     match_cfg! {
117         #[cfg(test)] => {
118             use core::option::Option as Option2;
119             fn works1() -> Option2<u32> { Some(1) }
120         }
121         _ => {
122             fn works1() -> Option<u32> { None }
123         }
124     }
125 
126     match_cfg! {
127         #[cfg(foo)] => {
128             fn works2() -> bool { false }
129         }
130         #[cfg(test)] => {
131             fn works2() -> bool { true }
132         }
133         _ => {
134             fn works2() -> bool { false }
135         }
136     }
137 
138     match_cfg! {
139         #[cfg(foo)] => {
140             fn works3() -> bool { false }
141         }
142         _ => {
143             fn works3() -> bool { true }
144         }
145     }
146 
147     match_cfg! {
148         #[cfg(test)] => {
149             use core::option::Option as Option3;
150             fn works4() -> Option3<u32> { Some(1) }
151         }
152     }
153 
154     match_cfg! {
155         #[cfg(foo)] => {
156             fn works5() -> bool { false }
157         }
158         #[cfg(test)] => {
159             fn works5() -> bool { true }
160         }
161     }
162 
163     #[test]
it_works()164     fn it_works() {
165         assert!(works1().is_some());
166         assert!(works2());
167         assert!(works3());
168         assert!(works4().is_some());
169         assert!(works5());
170     }
171 }
172