spottedcat_ecs_macro/
lib.rs

1
2use proc_macro::TokenStream;
3use quote::{quote};
4use syn::{
5    DeriveInput, 
6    Ident,
7    Field,
8};
9
10#[proc_macro_attribute]
11pub fn component(_attr: TokenStream, item: TokenStream) -> TokenStream {
12    let mut ast = syn::parse_macro_input!(item as DeriveInput);
13    impl_component(&mut ast)
14}
15
16fn impl_component(ast: &mut syn::DeriveInput) -> TokenStream {
17    let name = &ast.ident;
18    let inner_name = Ident::new(&format!("{}Inner", name), name.span());
19
20    let has_derive_default = ast.attrs.iter()
21        .filter(|attr| attr.path.is_ident("derive"))
22        .flat_map(|attr| match attr.parse_meta() {
23            Ok(syn::Meta::List(list)) => Some(list.nested.into_iter().collect::<Vec<_>>()),
24            _ => None
25        })
26        .flatten()
27        .any(|nested_meta| {
28            if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta {
29                path.is_ident("Default")
30            } else {
31                false
32            }
33        });
34
35    if !has_derive_default {
36        return syn::Error::new_spanned(
37            ast,
38            "#[component] requires #[derive(Default)]",
39        )
40        .to_compile_error()
41        .into();
42    }
43
44    // Get fields from the struct
45    let fields = match &ast.data {
46        syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields), .. }) => {
47            &fields.named
48        }
49        _ => {
50            return syn::Error::new_spanned(
51                ast,
52                "#[component] can only be used on structs with named fields",
53            )
54            .to_compile_error()
55            .into();
56        }
57    };
58
59    // Generate inner struct fields
60    let inner_fields = fields.iter().map(|f| {
61        let Field { ident, ty, .. } = f;
62        quote! {
63            pub #ident: #ty
64        }
65    });
66
67    // Generate getter and setter methods
68    let getter_setter_methods = fields.iter().map(|f| {
69        let name = f.ident.as_ref().unwrap();
70        let ty = &f.ty;
71        let get_name = Ident::new(&format!("get_{}", name), name.span());
72        let set_name = Ident::new(&format!("set_{}", name), name.span());
73
74        quote! {
75            pub fn #get_name(&self) -> Result<#ty, std::cell::BorrowError> {
76                match self.inner.try_borrow() {
77                    Ok(borrowed) => Ok(borrowed.#name.clone()),
78                    Err(e) => Err(e)
79                }
80            }
81            pub fn #set_name(&self, value: #ty) -> Result<(), std::cell::BorrowMutError> {
82                match self.inner.try_borrow_mut() {
83                    Ok(mut borrowed) => {
84                        borrowed.#name = value;
85                        Ok(())
86                    }
87                    Err(e) => Err(e)
88                }
89            }
90        }
91    });
92
93    // Generate the implementation
94    let attrs = ast.attrs.iter()
95        .filter(|attr| !attr.path.is_ident("component"))
96        .cloned()
97        .collect::<Vec<_>>();
98    
99    let expanded = quote! {
100        #(#attrs)*
101        pub struct #inner_name {
102            #(#inner_fields),*
103        }
104
105        #(#attrs)*
106        pub struct #name {
107            inner: std::sync::Arc<std::cell::RefCell<#inner_name>>,
108        }
109
110        impl #name {
111            pub fn new() -> Self {
112                let inner = #inner_name::default();
113                Self {
114                    inner: std::sync::Arc::new(std::cell::RefCell::new(inner)),
115                }
116            }
117
118            #(#getter_setter_methods)*
119        }
120
121        impl Component for #name {
122            fn get_type_id(&self) -> std::any::TypeId {
123                std::any::TypeId::of::<Self>()
124            }
125        }
126    };
127    
128    expanded.into()
129}