avian_derive/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
//! Provides derive implementations for [Avian Physics](https://github.com/Jondolf/avian).
use proc_macro::TokenStream;
use proc_macro_error2::{abort, emit_error, proc_macro_error};
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput};
// Modified macro from the discontinued Heron
// https://github.com/jcornaz/heron/blob/main/macros/src/lib.rs
/// A derive macro for defining physics layers using an enum.
///
/// Each variant of the enum represents a layer. Each layer has a unique bit determined by
/// the order of the variants. The bit value can be retrieved using the `to_bits` method.
///
/// # Requirements
///
/// - The enum must have at most 32 variants.
/// - The enum variants must not have any fields.
/// - The enum must have a default variant with the `#[default]` attribute.
/// - The first bit `1 << 0` will *always* be reserved for the default layer.
/// The bit values of the other layers are determined by their order in the enum, starting from `1 << 1`.
///
/// # Example
///
/// ```ignore
/// #[derive(PhysicsLayer, Clone, Copy, Debug, Default)]
/// enum GameLayer {
/// #[default]
/// Default, // Layer 0 - the default layer that objects are assigned to
/// Player, // Layer 1
/// Enemy, // Layer 2
/// Ground, // Layer 3
/// }
///
/// // The first bit is reserved for the default layer.
/// assert_eq!(GameLayer::default().to_bits(), 1 << 0);
///
/// // The `GameLayer::Ground` layer is the fourth layer, so its bit value is `1 << 3`.
/// assert_eq!(GameLayer::Ground.to_bits(), 1 << 3);
/// ```
#[proc_macro_error]
#[proc_macro_derive(PhysicsLayer)]
pub fn derive_physics_layer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_ident = input.ident;
fn non_enum_item_error(span: proc_macro2::Span) -> TokenStream {
abort!(span, "only enums can automatically derive `PhysicsLayer`");
}
let variants = match &input.data {
Data::Enum(data) => &data.variants,
Data::Struct(data) => {
return non_enum_item_error(data.struct_token.span);
}
Data::Union(data) => {
return non_enum_item_error(data.union_token.span);
}
};
if variants.len() > 32 {
emit_error!(
enum_ident,
"`PhysicsLayer` only supports a maximum of 32 layers"
);
}
let mut default_variant_index = None;
for (i, variant) in variants.iter().enumerate() {
for attr in variant.attrs.iter() {
if attr.path().is_ident("default") {
if default_variant_index.is_some() {
emit_error!(enum_ident, "multiple defaults");
break;
}
default_variant_index = Some(i);
}
}
}
let Some(default_variant_index) = default_variant_index else {
abort!(
enum_ident,
"`PhysicsLayer` enums must derive `Default` and have a variant annotated with the `#[default]` attribute.";
note = "Manually implementing `Default` using `impl Default for FooLayer` is not supported."
);
};
// Move the default variant to the front (this probably isn't the best way to do this)
let mut variants = variants.iter().collect::<Vec<_>>();
let default_variant = variants.remove(default_variant_index);
variants.insert(0, default_variant);
let to_bits_result: Result<Vec<_>, _> = variants
.iter()
.enumerate()
.map(|(index, variant)| {
if !variant.fields.is_empty() {
return Err(variant.fields.span());
}
let bits: u32 = 1 << index;
let ident = &variant.ident;
Ok(quote! { #enum_ident::#ident => #bits, })
})
.collect();
let to_bits = match to_bits_result {
Ok(tokens) => tokens,
Err(span) => {
abort!(
span,
"can only derive `PhysicsLayer` for enums without fields"
);
}
};
let all_bits: u32 = if variants.len() == 32 {
0xffffffff
} else {
(1 << variants.len()) - 1
};
let expanded = quote! {
impl PhysicsLayer for #enum_ident {
fn all_bits() -> u32 {
#all_bits
}
fn to_bits(&self) -> u32 {
match self {
#(#to_bits)*
}
}
}
};
TokenStream::from(expanded)
}