bevy_platform/
cfg.rs

1//! Provides helpful configuration macros, allowing detection of platform features such as
2//! [`alloc`](crate::cfg::alloc) or [`std`](crate::cfg::std) without explicit features.
3
4/// Provides a `match`-like expression similar to [`cfg_if`] and based on the experimental
5/// [`cfg_match`].
6/// The name `switch` is used to avoid conflict with the `match` keyword.
7/// Arms are evaluated top to bottom, and an optional wildcard arm can be provided if no match
8/// can be made.
9///
10/// An arm can either be:
11/// - a `cfg(...)` pattern (e.g., `feature = "foo"`)
12/// - a wildcard `_`
13/// - an alias defined using [`define_alias`]
14///
15/// Common aliases are provided by [`cfg`](crate::cfg).
16/// Note that aliases are evaluated from the context of the defining crate, not the consumer.
17///
18/// # Examples
19///
20/// ```
21/// # use bevy_platform::cfg;
22/// # fn log(_: &str) {}
23/// # fn foo(_: &str) {}
24/// #
25/// cfg::switch! {
26///     #[cfg(feature = "foo")] => {
27///         foo("We have the `foo` feature!")
28///     }
29///     cfg::std => {
30///         extern crate std;
31///         std::println!("No `foo`, but we have `std`!");
32///     }
33///     _ => {
34///         log("Don't have `std` or `foo`");
35///     }
36/// }
37/// ```
38///
39/// [`cfg_if`]: https://crates.io/crates/cfg-if
40/// [`cfg_match`]: https://github.com/rust-lang/rust/issues/115585
41#[doc(inline)]
42pub use crate::switch;
43
44/// Defines an alias for a particular configuration.
45/// This has two advantages over directly using `#[cfg(...)]`:
46///
47/// 1. Complex configurations can be abbreviated to more meaningful shorthand.
48/// 2. Features are evaluated in the context of the _defining_ crate, not the consuming.
49///
50/// The second advantage is a particularly powerful tool, as it allows consuming crates to use
51/// functionality in a defining crate regardless of what crate in the dependency graph enabled the
52/// relevant feature.
53///
54/// For example, consider a crate `foo` that depends on another crate `bar`.
55/// `bar` has a feature "`faster_algorithms`".
56/// If `bar` defines a "`faster_algorithms`" alias:
57///
58/// ```ignore
59/// define_alias! {
60///     #[cfg(feature = "faster_algorithms")] => { faster_algorithms }
61/// }
62/// ```
63///
64/// Now, `foo` can gate its usage of those faster algorithms on the alias, avoiding the need to
65/// expose its own "`faster_algorithms`" feature.
66/// This also avoids the unfortunate situation where one crate activates "`faster_algorithms`" on
67/// `bar` without activating that same feature on `foo`.
68///
69/// Once an alias is defined, there are 4 ways you can use it:
70///
71/// 1. Evaluate with no contents to return a `bool` indicating if the alias is active.
72///    ```
73///    # use bevy_platform::cfg;
74///    if cfg::std!() {
75///        // Have `std`!
76///    } else {
77///        // No `std`...
78///    }
79///    ```
80/// 2. Pass a single code block which will only be compiled if the alias is active.
81///    ```
82///    # use bevy_platform::cfg;
83///    cfg::std! {
84///        // Have `std`!
85///    # ()
86///    }
87///    ```
88/// 3. Pass a single `if { ... } else { ... }` expression to conditionally compile either the first
89///    or the second code block.
90///    ```
91///    # use bevy_platform::cfg;
92///    cfg::std! {
93///        if {
94///            // Have `std`!
95///        } else {
96///            // No `std`...
97///        }
98///    }
99///    ```
100/// 4. Use in a [`switch`] arm for more complex conditional compilation.
101///    ```
102///    # use bevy_platform::cfg;
103///    cfg::switch! {
104///        cfg::std => {
105///            // Have `std`!
106///        }
107///        cfg::alloc => {
108///            // No `std`, but do have `alloc`!
109///        }
110///        _ => {
111///            // No `std` or `alloc`...
112///        }
113///    }
114///    ```
115#[doc(inline)]
116pub use crate::define_alias;
117
118/// Macro which represents an enabled compilation condition.
119#[doc(inline)]
120pub use crate::enabled;
121
122/// Macro which represents a disabled compilation condition.
123#[doc(inline)]
124pub use crate::disabled;
125
126#[doc(hidden)]
127#[macro_export]
128macro_rules! switch {
129    ({ $($tt:tt)* }) => {{
130        $crate::switch! { $($tt)* }
131    }};
132    (_ => { $($output:tt)* }) => {
133        $($output)*
134    };
135    (
136        $cond:path => $output:tt
137        $($( $rest:tt )+)?
138    ) => {
139        $cond! {
140            if {
141                $crate::switch! { _ => $output }
142            } else {
143                $(
144                    $crate::switch! { $($rest)+ }
145                )?
146            }
147        }
148    };
149    (
150        #[cfg($cfg:meta)] => $output:tt
151        $($( $rest:tt )+)?
152    ) => {
153        #[cfg($cfg)]
154        $crate::switch! { _ => $output }
155        $(
156            #[cfg(not($cfg))]
157            $crate::switch! { $($rest)+ }
158        )?
159    };
160}
161
162#[doc(hidden)]
163#[macro_export]
164macro_rules! disabled {
165    () => { false };
166    (if { $($p:tt)* } else { $($n:tt)* }) => { $($n)* };
167    ($($p:tt)*) => {};
168}
169
170#[doc(hidden)]
171#[macro_export]
172macro_rules! enabled {
173    () => { true };
174    (if { $($p:tt)* } else { $($n:tt)* }) => { $($p)* };
175    ($($p:tt)*) => { $($p)* };
176}
177
178#[doc(hidden)]
179#[macro_export]
180macro_rules! define_alias {
181    (
182        #[cfg($meta:meta)] => $p:ident
183        $(, $( $rest:tt )+)?
184    ) => {
185        $crate::define_alias! {
186            #[cfg($meta)] => { $p }
187            $(
188                $($rest)+
189            )?
190        }
191    };
192    (
193        #[cfg($meta:meta)] => $p:ident,
194        $($( $rest:tt )+)?
195    ) => {
196        $crate::define_alias! {
197            #[cfg($meta)] => { $p }
198            $(
199                $($rest)+
200            )?
201        }
202    };
203    (
204        #[cfg($meta:meta)] => {
205            $(#[$p_meta:meta])*
206            $p:ident
207        }
208        $($( $rest:tt )+)?
209    ) => {
210        $crate::switch! {
211            #[cfg($meta)] => {
212                $(#[$p_meta])*
213                #[doc(inline)]
214                ///
215                #[doc = concat!("This macro passes the provided code because `#[cfg(", stringify!($meta), ")]` is currently active.")]
216                pub use $crate::enabled as $p;
217            }
218            _ => {
219                $(#[$p_meta])*
220                #[doc(inline)]
221                ///
222                #[doc = concat!("This macro suppresses the provided code because `#[cfg(", stringify!($meta), ")]` is _not_ currently active.")]
223                pub use $crate::disabled as $p;
224            }
225        }
226
227        $(
228            $crate::define_alias! {
229                $($rest)+
230            }
231        )?
232    }
233}
234
235define_alias! {
236    #[cfg(feature = "alloc")] => {
237        /// Indicates the `alloc` crate is available and can be used.
238        alloc
239    }
240    #[cfg(feature = "std")] => {
241        /// Indicates the `std` crate is available and can be used.
242        std
243    }
244    #[cfg(panic = "unwind")] => {
245        /// Indicates that a [`panic`] will be unwound, and can be potentially caught.
246        panic_unwind
247    }
248    #[cfg(panic = "abort")] => {
249        /// Indicates that a [`panic`] will lead to an abort, and cannot be caught.
250        panic_abort
251    }
252    #[cfg(all(target_arch = "wasm32", feature = "web"))] => {
253        /// Indicates that this target has access to browser APIs.
254        web
255    }
256    #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] => {
257        /// Indicates that this target has access to a native implementation of `Arc`.
258        arc
259    }
260    #[cfg(feature = "critical-section")] => {
261        /// Indicates `critical-section` is available.
262        critical_section
263    }
264}