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}