bevy_console_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5#[proc_macro_derive(ConsoleCommand, attributes(command))]
6pub fn derive_clap_command(input: TokenStream) -> TokenStream {
7    let derive_input = parse_macro_input!(input as DeriveInput);
8
9    let name_string = get_command_name(&derive_input);
10    let name = &derive_input.ident;
11    let generics = derive_input.generics;
12    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
13
14    TokenStream::from(quote! {
15        impl #impl_generics bevy_console::NamedCommand for #name #ty_generics #where_clause {
16            fn name() -> &'static str {
17                #name_string
18            }
19        }
20
21        impl #impl_generics bevy::prelude::Resource for #name #ty_generics #where_clause {};
22    })
23}
24
25fn get_command_name(input: &DeriveInput) -> syn::LitStr {
26    input
27        .attrs
28        .iter()
29        .find_map(|attr| {
30            if attr.path.is_ident("command") {
31                if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
32                    return list.nested.iter().find_map(|meta| {
33                        if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = meta {
34                            Some(nv.lit.clone())
35                        } else {
36                            None
37                        }
38                    });
39                }
40            }
41            None
42        })
43        .map(|lit| {
44            if let syn::Lit::Str(str) = lit {
45                str
46            } else {
47                panic!("Expected string literal as command name");
48            }
49        })
50        .unwrap_or_else(|| syn::LitStr::new(&input.ident.to_string(), input.ident.span()))
51}