Skip to main content

cu29_reflect_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::{ToTokens, quote};
3use syn::{Attribute, DeriveInput, parse_macro_input};
4
5/// Lightweight fallback derive used when Copper reflect is disabled.
6///
7/// This keeps accepting `#[reflect(...)]` helper attributes without pulling
8/// `bevy_reflect`, while still emitting the `TypePath` impl that runtime code
9/// generation now relies on for schema/introspection metadata.
10#[proc_macro_derive(Reflect, attributes(reflect))]
11pub fn derive_reflect(input: TokenStream) -> TokenStream {
12    let input = parse_macro_input!(input as DeriveInput);
13    derive_reflect_impl(&input).into()
14}
15
16fn derive_reflect_impl(input: &DeriveInput) -> proc_macro2::TokenStream {
17    if reflect_disables_type_path(&input.attrs) {
18        return proc_macro2::TokenStream::new();
19    }
20
21    let ident = &input.ident;
22    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
23
24    quote! {
25        impl #impl_generics cu29::reflect::TypePath for #ident #ty_generics #where_clause {}
26    }
27}
28
29fn reflect_disables_type_path(attrs: &[Attribute]) -> bool {
30    attrs
31        .iter()
32        .filter(|attr| attr.path().is_ident("reflect"))
33        .any(|attr| {
34            attr.meta
35                .to_token_stream()
36                .to_string()
37                .chars()
38                .filter(|ch| !ch.is_whitespace())
39                .collect::<String>()
40                .contains("type_path=false")
41        })
42}
43
44#[cfg(test)]
45mod tests {
46    use super::{derive_reflect_impl, reflect_disables_type_path};
47    use syn::parse_quote;
48
49    #[test]
50    fn emits_type_path_impl_by_default() {
51        let input = parse_quote! {
52            struct Payload;
53        };
54
55        let output = derive_reflect_impl(&input).to_string();
56
57        assert!(output.contains("impl cu29 :: reflect :: TypePath for Payload"));
58    }
59
60    #[test]
61    fn respects_type_path_false() {
62        let input = parse_quote! {
63            #[reflect(from_reflect = false, type_path = false)]
64            struct Payload<T>(T);
65        };
66
67        assert!(derive_reflect_impl(&input).is_empty());
68        assert!(reflect_disables_type_path(&input.attrs));
69    }
70}