avian_derive/
lib.rs

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