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