fourthage_mud_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput, Meta};
4
5#[proc_macro_derive(ComponentStorage, attributes(component))]
17pub fn derive_component_storage(input: TokenStream) -> TokenStream {
18 let input = parse_macro_input!(input as DeriveInput);
19
20 let name = &input.ident;
21 let field_name = match extract_field_name(&input.attrs) {
22 Ok(s) => s,
23 Err(_) => {
24 return syn::Error::new_spanned(
25 name,
26 "ComponentStorage derive requires #[component(field = \"field_name\")] attribute",
27 )
28 .to_compile_error().into();
29 }
30 };
31
32 let field_ident = syn::Ident::new(&field_name, name.span());
33
34 let expanded = quote! {
35 impl ComponentStorage for #name {
36 fn get<'a>(entities: &'a EntityRegistryInternal, entity: &EntityId) -> Option<&'a Self>
37 where
38 Self: Sized,
39 {
40 entities.#field_ident.get(entity)
41 }
42
43 fn update(entities: &mut EntityRegistryInternal, entity: &EntityId, component: Self)
44 where
45 Self: Sized,
46 {
47 entities.#field_ident.insert(entity.clone(), component);
48 }
49
50 fn remove(entities: &mut EntityRegistryInternal, entity: &EntityId)
51 where
52 Self: Sized,
53 {
54 entities.#field_ident.remove(entity);
55 }
56
57 fn storage(entities: &EntityRegistryInternal) -> &HashMap<EntityId, Self>
58 where
59 Self: Sized,
60 {
61 &entities.#field_ident
62 }
63 }
64 };
65
66 TokenStream::from(expanded)
67}
68
69fn extract_field_name(attrs: &[syn::Attribute]) -> Result<String, ()> {
70 for attr in attrs {
71 if attr.path().is_ident("component") {
72 if let Meta::List(meta_list) = &attr.meta {
74 let content: syn::MetaNameValue = syn::parse2(meta_list.tokens.clone()).map_err(|_| ())?;
76 if content.path.is_ident("field") {
77 if let syn::Expr::Lit(syn::ExprLit {
78 lit: syn::Lit::Str(lit_str),
79 ..
80 }) = &content.value
81 {
82 return Ok(lit_str.value());
83 }
84 }
85 }
86 }
87 }
88 Err(())
89}