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}