cfg_if/lib.rs
1//! A macro for defining `#[cfg]` if-else statements.
2//!
3//! The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C
4//! preprocessor macro by allowing definition of a cascade of `#[cfg]` cases,
5//! emitting the implementation which matches first.
6//!
7//! This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
8//! without having to rewrite each clause multiple times.
9//!
10//! # Example
11//!
12//! ```
13//! cfg_if::cfg_if! {
14//! if #[cfg(unix)] {
15//! fn foo() { /* unix specific functionality */ }
16//! } else if #[cfg(target_pointer_width = "32")] {
17//! fn foo() { /* non-unix, 32-bit functionality */ }
18//! } else {
19//! fn foo() { /* fallback implementation */ }
20//! }
21//! }
22//!
23//! # fn main() {}
24//! ```
25
26#![no_std]
27#![doc(html_root_url = "https://docs.rs/cfg-if")]
28#![deny(missing_docs)]
29#![cfg_attr(test, deny(warnings))]
30#![cfg_attr(test, allow(unexpected_cfgs))] // we test with features that do not exist
31
32/// The main macro provided by this crate. See crate documentation for more
33/// information.
34#[macro_export]
35macro_rules! cfg_if {
36 // match if/else chains with a final `else`
37 (
38 $(
39 if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
40 ) else+
41 else { $( $e_tokens:tt )* }
42 ) => {
43 $crate::cfg_if! {
44 @__items () ;
45 $(
46 (( $i_meta ) ( $( $i_tokens )* )) ,
47 )+
48 (() ( $( $e_tokens )* )) ,
49 }
50 };
51
52 // match if/else chains lacking a final `else`
53 (
54 if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
55 $(
56 else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* }
57 )*
58 ) => {
59 $crate::cfg_if! {
60 @__items () ;
61 (( $i_meta ) ( $( $i_tokens )* )) ,
62 $(
63 (( $e_meta ) ( $( $e_tokens )* )) ,
64 )*
65 }
66 };
67
68 // Internal and recursive macro to emit all the items
69 //
70 // Collects all the previous cfgs in a list at the beginning, so they can be
71 // negated. After the semicolon are all the remaining items.
72 (@__items ( $( $_:meta , )* ) ; ) => {};
73 (
74 @__items ( $( $no:meta , )* ) ;
75 (( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
76 $( $rest:tt , )*
77 ) => {
78 // Emit all items within one block, applying an appropriate #[cfg]. The
79 // #[cfg] will require all `$yes` matchers specified and must also negate
80 // all previous matchers.
81 #[cfg(all(
82 $( $yes , )?
83 not(any( $( $no ),* ))
84 ))]
85 $crate::cfg_if! { @__identity $( $tokens )* }
86
87 // Recurse to emit all other items in `$rest`, and when we do so add all
88 // our `$yes` matchers to the list of `$no` matchers as future emissions
89 // will have to negate everything we just matched as well.
90 $crate::cfg_if! {
91 @__items ( $( $no , )* $( $yes , )? ) ;
92 $( $rest , )*
93 }
94 };
95
96 // Internal macro to make __apply work out right for different match types,
97 // because of how macros match/expand stuff.
98 (@__identity $( $tokens:tt )* ) => {
99 $( $tokens )*
100 };
101}
102
103#[cfg(test)]
104mod tests {
105 cfg_if! {
106 if #[cfg(test)] {
107 use core::option::Option as Option2;
108 fn works1() -> Option2<u32> { Some(1) }
109 } else {
110 fn works1() -> Option<u32> { None }
111 }
112 }
113
114 cfg_if! {
115 if #[cfg(foo)] {
116 fn works2() -> bool { false }
117 } else if #[cfg(test)] {
118 fn works2() -> bool { true }
119 } else {
120 fn works2() -> bool { false }
121 }
122 }
123
124 cfg_if! {
125 if #[cfg(foo)] {
126 fn works3() -> bool { false }
127 } else {
128 fn works3() -> bool { true }
129 }
130 }
131
132 cfg_if! {
133 if #[cfg(test)] {
134 use core::option::Option as Option3;
135 fn works4() -> Option3<u32> { Some(1) }
136 }
137 }
138
139 cfg_if! {
140 if #[cfg(foo)] {
141 fn works5() -> bool { false }
142 } else if #[cfg(test)] {
143 fn works5() -> bool { true }
144 }
145 }
146
147 #[test]
148 fn it_works() {
149 assert!(works1().is_some());
150 assert!(works2());
151 assert!(works3());
152 assert!(works4().is_some());
153 assert!(works5());
154 }
155
156 #[test]
157 #[allow(clippy::assertions_on_constants)]
158 fn test_usage_within_a_function() {
159 cfg_if! {if #[cfg(debug_assertions)] {
160 // we want to put more than one thing here to make sure that they
161 // all get configured properly.
162 assert!(cfg!(debug_assertions));
163 assert_eq!(4, 2+2);
164 } else {
165 assert!(works1().is_some());
166 assert_eq!(10, 5+5);
167 }}
168 }
169
170 #[allow(dead_code)]
171 trait Trait {
172 fn blah(&self);
173 }
174
175 #[allow(dead_code)]
176 struct Struct;
177
178 impl Trait for Struct {
179 cfg_if! {
180 if #[cfg(feature = "blah")] {
181 fn blah(&self) {
182 unimplemented!();
183 }
184 } else {
185 fn blah(&self) {
186 unimplemented!();
187 }
188 }
189 }
190 }
191}