1 unstable release
new 0.1.0 | Dec 13, 2024 |
---|---|
0.0.0 |
|
#341 in Procedural macros
126 downloads per month
19KB
308 lines
pragma
Macro
Mostly an experimentation macro. The pragma!
macro provides a powerful experimental inline DSL for conditional item inclusion in Rust code. It lets you specify conditions on functions, modules, or other items and automatically generates #[cfg(...)]
attributes with supporting logic (including and
, or
, not
, and parenthesized groups) for complex conditional compilation scenarios.
Full example
Here's a full example that utilizes all of pragma
's language extensions:
use pragma::pragma;
pragma! {
/// this function is public if `target_pointer_width = "64"`, otherwise private
pub (if target_pointer_width = "64") fn wide_pointer_fn() {
println!("64-bit pointer width!");
}
/// a module that is included if `test` is enabled, private otherwise
pub (if test) mod test_mod {
pub fn inside() { println!("Testing mode!"); }
}
/// a function that only appears if `target_pointer_width = "32"`
(if target_pointer_width = "32") fn narrow_pointer_fn() {}
/// a more complex condition that checks multiple cfg attributes
(if target_pointer_width = "64" and (target_pointer_width = "16" or not(debug_assertions))) fn fancy_fn() {
println!("Fancy conditional logic!");
}
/// an unconditional item
static _UNCONDITIONAL: &'static str = "Always here!";
}
Language extensions
-
Simple Conditional Items: You can write:
pragma! { (if test) fn do_something() { println!("Running with `test` feature!"); } }
-
Visibility-Aware Items: If you specify a visibility modifier before
(if condition)
, two versions of the item are generated:- A public version with
#[cfg(condition)]
- A private version with
#[cfg(not(condition))]
For example:
pragma! { /// this function is public if `test` is enabled, otherwise a private fallback is included pub (if test) fn conditional_public() {} }
This expands to:
#[cfg(test)] pub fn conditional_public() {} #[cfg(not(test))] fn conditional_public() {}
Without any visibility specified, only a single
#[cfg(condition)]
version of the item is generated:pragma! { (if test) fn single_version() {} }
Expands to:
#[cfg(test)] fn single_version() {}
- A public version with
-
Unconditional Items: You can mix normal, unconditional items with conditional ones. For example:
pragma! { (if test) fn conditional_fn() {} static GLOBAL_VAR: i32 = 42; // no conditions here, always included }
-
Modules With Conditions: You can also apply conditions to entire modules:
pragma! { pub (if test) mod conditional_mod { fn inside() {} } }
This expands to a public module if
test
is enabled, or a private module otherwise. -
Complex Conditions: The conditions inside
(if ...)
support a custom DSL withand
,or
,not
, parentheses for grouping, and key-value checks:- Use
and
andor
for logical conjunction and disjunction. - Wrap conditions in parentheses to control evaluation.
- Use
not(...)
for negation. - Use
key = "value"
for cfg key-value pairs, and barekey
for boolean cfg options.
Examples:
pragma! { // matches if target_pointer_width = "64" or target_pointer_width = "16" (if target_pointer_width = "64" or target_pointer_width = "16") fn special_fn() {} // matches if target_pointer_width = "64" and (target_pointer_width = "16" or not(debug_assertions)) (if target_pointer_width = "64" and (target_pointer_width = "16" or not(debug_assertions))) fn complex_fn() {} // you can also negate conditions (if not(test)) fn not_test_fn() {} }
Expands to:
#[cfg(all(target_pointer_width = "64", target_pointer_width = "16"))] fn special_fn() {} #[cfg(all( target_pointer_width = "64", any(target_pointer_width = "16", not(debug_assertions)) ))] fn complex_fn() {} #[cfg(not(test))] fn not_test_fn() {}
- Use
Motivation
If you're wondering why this was written in the first place, then the answer is:
(1) having used C I have been very used to pre-processor directives and while the same can often be done with #[cfg(..)]
I feel "more at home" using something more C-style. Of course, ideally I would be able to apply it to a whole module file without explicit invocation, but from my understanding of Rust macros, that isn't currently possible.
(2) experimenting to test the limits of what can and cannot be done with procedural macros
I published this here if someone finds it useful.
Say you have something like this in C:
#ifdef FEATURE_FLAG
// struct definition when FEATURE_FLAG is enabled
struct MyStruct {
int a;
double b;
};
#else
// struct definition when FEATURE_FLAG is not enabled
struct MyStruct {
char x;
float y;
};
#endif
Then you can write something similar in Rust using this macro:
pragma! {
(if feature = FEATURE_FLAG) struct MyStruct {
a: i32,
b: f64,
}
(if not(feature = FEATURE_FLAG)) struct MyStruct {
x: i8,
y: f32
}
}
In vanilla Rust, this would be:
#[cfg(feature = "FEATURE_FLAG")]
struct MyStruct { .. }
#[cfg(not(feature = "FEATURE_FLAG"))]
struct MyStruct { .. }
License
This crate is distributed under the Apache-2.0 License.
Dependencies
~1.5MB
~37K SLoC