use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput, Error, Expr, LitStr, Result}; #[proc_macro_derive(Command, attributes(command))] pub fn derive_command(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand(&input) .unwrap_or_else(Error::into_compile_error) .into() } fn expand(input: &DeriveInput) -> Result { let attr = input .attrs .iter() .find(|attr| attr.path().is_ident("command")) .ok_or_else(|| { Error::new_spanned(&input.ident, "missing #[command(id = ...)] attribute") })?; let mut id: Option = None; let mut name: Option = None; attr.parse_nested_meta(|meta| { if meta.path.is_ident("id") { let value: Expr = meta.value()?.parse()?; id = Some(quote!(#value)); return Ok(()); } if meta.path.is_ident("name") { let value: LitStr = meta.value()?.parse()?; name = Some(value.value()); return Ok(()); } Err(meta.error("unsupported #[command(...)] key")) })?; let id = id.ok_or_else(|| Error::new_spanned(attr, "#[command(...)] requires `id`"))?; let ident = &input.ident; let name = name.unwrap_or_else(|| ident.to_string()); Ok(quote! { impl ::logic::commands::CommandMeta for #ident { const COMMAND_TYPE: i32 = #id; const NAME: &'static str = #name; } ::titan::inventory::submit! { ::logic::commands::CommandRegistryEntry::of::<#ident>() } }) }