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! {
160            if #[cfg(debug_assertions)] {
161                // we want to put more than one thing here to make sure that they
162                // all get configured properly.
163                assert!(cfg!(debug_assertions));
164                assert_eq!(4, 2 + 2);
165            } else {
166                assert!(works1().is_some());
167                assert_eq!(10, 5 + 5);
168            }
169        }
170    }
171
172    #[allow(dead_code)]
173    trait Trait {
174        fn blah(&self);
175    }
176
177    #[allow(dead_code)]
178    struct Struct;
179
180    impl Trait for Struct {
181        cfg_if! {
182            if #[cfg(feature = "blah")] {
183                fn blah(&self) { unimplemented!(); }
184            } else {
185                fn blah(&self) { unimplemented!(); }
186            }
187        }
188    }
189}