devela/code/util/
cfg_if.rs

1// devela::code::util::cfg_if
2//
3//! cfg_if macro.
4//
5// Original source:
6// https://github.com/rust-lang/cfg-if/blob/4edf32745bf5039868f1f258f7fa94603eec5cf5/src/lib.rs
7//
8// MODIFICATIONS
9// - support multiple conditions. (https://github.com/rust-lang/cfg-if/pull/78)
10// - update docs & misc. refactoring.
11
12/// A macro for defining `#[cfg]` if-else statements.
13///
14/// Allows definition of a cascade of `#[cfg]` cases,
15/// emitting the implementation which matches first.
16///
17/// # Example
18/// ```
19/// # use devela::cfg_if;
20/// cfg_if! {
21///     if #[cfg(unix)] {
22///         fn foo() { /* unix specific functionality */ }
23///     } else if #[cfg(target_pointer_width = "32")] {
24///         fn foo() { /* non-unix, 32-bit functionality */ }
25///     } else {
26///         fn foo() { /* fallback implementation */ }
27///     }
28///
29///     // there can be multiple conditions
30///     if #[cfg(feature = "bar")] {
31///         fn bar() {}
32///     }
33/// }
34/// ```
35#[doc = crate::doc_!(vendor: "cfg-if")]
36#[macro_export]
37#[cfg_attr(cargo_primary_package, doc(hidden))]
38macro_rules! cfg_if {
39    // match if/else chains with a final `else`
40    ( $( if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } ) else+
41      else { $( $e_tokens:tt )* }
42    ) => {
43        $crate::cfg_if! {
44            @__items () ;
45            $( (( $i_meta ) ( $( $i_tokens )* )) , )+
46            (() ( $( $e_tokens )* )) ,
47        }
48    };
49    // allow multiple conditions
50    ( $( if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } ) else+
51      else { $( $e_tokens:tt )* }
52      if $($extra_conditions:tt)+
53    ) => {
54        $crate::cfg_if! {
55            @__items () ;
56            $( (( $i_meta ) ( $( $i_tokens )* )) , )+
57            (() ( $( $e_tokens )* )) ,
58        }
59        $crate::cfg_if! { if $($extra_conditions)+ }
60    };
61
62    // match if/else chains lacking a final `else`
63    ( if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
64      $( else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* } )*
65    ) => {
66        $crate::cfg_if! {
67            @__items () ;
68            (( $i_meta ) ( $( $i_tokens )* )) ,
69            $( (( $e_meta ) ( $( $e_tokens )* )) , )*
70        }
71    };
72    // allow multiple conditions
73    ( if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
74      $( else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* } )*
75      if $($extra_conditions:tt)+
76    ) => {
77        $crate::cfg_if! {
78            @__items () ;
79            (( $i_meta ) ( $( $i_tokens )* )) ,
80            $( (( $e_meta ) ( $( $e_tokens )* )) , )*
81        }
82        $crate::cfg_if! { if $($extra_conditions)+ }
83    };
84
85    // Internal and recursive macro to emit all the items
86    //
87    // Collects all the previous cfgs in a list at the beginning, so they can be
88    // negated. After the semicolon are all the remaining items.
89    (@__items ( $( $_:meta , )* ) ; ) => {};
90    ( @__items ( $( $no:meta , )* ) ;
91      (( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
92      $( $rest:tt , )*
93    ) => {
94        // Emit all items within one block, applying an appropriate #[cfg]. The
95        // #[cfg] will require all `$yes` matchers specified and must also negate
96        // all previous matchers.
97        #[cfg(all(
98            $( $yes , )?
99            not(any( $( $no ),* ))
100        ))]
101        $crate::cfg_if! { @__identity $( $tokens )* }
102
103        // Recurse to emit all other items in `$rest`, and when we do so add all
104        // our `$yes` matchers to the list of `$no` matchers as future emissions
105        // will have to negate everything we just matched as well.
106        $crate::cfg_if! {
107            @__items ( $( $no , )* $( $yes , )? ) ;
108            $( $rest , )*
109        }
110    };
111
112    // Internal macro to make __apply work out right for different match types,
113    // because of how macros match/expand stuff.
114    (@__identity $( $tokens:tt )* ) => {
115        $( $tokens )*
116    };
117}
118#[doc(inline)]
119pub use cfg_if;
120
121#[cfg(test)]
122mod test_cfg_if {
123    #![allow(dead_code, unexpected_cfgs)]
124
125    use crate::cfg_if;
126
127    cfg_if! {
128        if #[cfg(test)] {
129            use core::option::Option as Option2;
130            fn works1() -> Option2<u32> { Some(1) }
131        } else {
132            fn works1() -> Option<u32> { None }
133        }
134    }
135
136    cfg_if! {
137        if #[cfg(foo)] {
138            fn works2() -> bool { false }
139        } else if #[cfg(test)] {
140            fn works2() -> bool { true }
141        } else {
142            fn works2() -> bool { false }
143        }
144    }
145
146    cfg_if! {
147        if #[cfg(foo)] {
148            fn works3() -> bool { false }
149        } else {
150            fn works3() -> bool { true }
151        }
152    }
153
154    cfg_if! {
155        if #[cfg(test)] {
156            use core::option::Option as Option3;
157            fn works4() -> Option3<u32> { Some(1) }
158        }
159    }
160
161    cfg_if! {
162        if #[cfg(foo)] {
163            fn works5() -> bool { false }
164        } else if #[cfg(test)] {
165            fn works5() -> bool { true }
166        }
167    }
168
169    // multiple conditions
170    cfg_if! {
171        if #[cfg(foo)] {
172            fn works6() -> bool { false }
173        } else if #[cfg(test)] {
174            fn works6() -> bool { true }
175        }
176        if #[cfg(test)] {
177            fn works7() -> bool { true }
178        } else {
179            fn works7() -> bool { false }
180        }
181    }
182    cfg_if! {
183        if #[cfg(test)] {
184            fn works8() -> bool { true }
185        } else if #[cfg(foo)] {
186            fn works8() -> bool { false }
187        }
188        if #[cfg(foo)] {
189            fn works9() -> bool { false }
190        } else if #[cfg(test)] {
191            fn works9() -> bool { true }
192        }
193    }
194
195    #[test]
196    fn it_works() {
197        assert!(works1().is_some());
198        assert!(works2());
199        assert!(works3());
200        assert!(works4().is_some());
201        assert!(works5());
202    }
203
204    #[test]
205    #[allow(clippy::assertions_on_constants)]
206    fn test_usage_within_a_function() {
207        cfg_if! {if #[cfg(debug_assertions)] {
208            // we want to put more than one thing here to make sure that they
209            // all get configured properly.
210            assert!(cfg!(debug_assertions));
211            assert_eq!(4, 2+2);
212        } else {
213            assert!(works1().is_some());
214            assert_eq!(10, 5+5);
215        }}
216    }
217
218    #[allow(dead_code)]
219    trait Trait {
220        fn blah(&self);
221    }
222
223    #[allow(dead_code)]
224    struct Struct;
225
226    impl Trait for Struct {
227        cfg_if! {
228            if #[cfg(feature = "blah")] {
229                fn blah(&self) {
230                    unimplemented!();
231                }
232            } else {
233                fn blah(&self) {
234                    unimplemented!();
235                }
236            }
237        }
238    }
239}