af_bevy_plugin_macro/
lib.rs

1use convert_case::{Case, Casing};
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::parse::{Parse, ParseStream, Result};
5use syn::{parse_macro_input, Ident, Token};
6struct Input {
7    head: Ident,
8    items: Vec<Ident>,
9}
10impl Parse for Input {
11    fn parse(input: ParseStream) -> Result<Self> {
12        let mut items = Vec::new();
13        let head = input.parse()?;
14        input.parse::<Token![,]>()?;
15        while !input.is_empty() {
16            items.push(input.parse()?);
17            if !input.is_empty() {
18                input.parse::<Token![,]>()?;
19            }
20        }
21        Ok(Input { head, items })
22    }
23}
24
25#[proc_macro]
26pub fn bevy_plugin_group(input: TokenStream) -> TokenStream {
27    let input = parse_macro_input!(input as Input);
28    let snake_items = &input.items;
29    let head = &input.head;
30
31    let plugin_items = snake_items.iter().map(|ident| {
32        Ident::new(
33            &(String::from(&ident.to_string().to_case(Case::Pascal)) + "Plugin"),
34            ident.span(),
35        )
36    });
37
38    let plugin_group_name = Ident::new(
39        &(String::from(head.to_string().to_case(Case::Pascal)) + "Plugins"),
40        head.span(),
41    );
42    TokenStream::from(quote! {
43        #(
44            pub mod #snake_items;
45            pub use #snake_items::*;
46        )*
47
48        pub struct #plugin_group_name;
49        use bevy::{app::PluginGroupBuilder, app::PluginGroup};
50        impl PluginGroup for #plugin_group_name {
51            fn build(self) -> PluginGroupBuilder {
52                PluginGroupBuilder::start::<Self>()
53                    #(
54                        .add(#snake_items::#plugin_items)
55                    )*
56            }
57        }
58
59    })
60}