hephae-utils-derive 0.7.2

Hephae's generic utility module proc-macro derive.
Documentation
use hephae_macros::{
    Manifest,
    proc_macro2::TokenStream,
    quote::quote,
    syn,
    syn::{
        Attribute, Block, Ident, Meta, Path, Stmt, Token, Visibility,
        parse::{Parse, ParseStream},
    },
};

struct Syntax {
    attrs: Vec<Attribute>,
    vis: Visibility,
    name: Ident,
    target: Path,
    param: Ident,
    block: Vec<Stmt>,
}

impl Parse for Syntax {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;

        let vis = input.parse()?;
        input.parse::<Token![trait]>()?;
        let name = input.parse()?;
        input.parse::<Token![for]>()?;
        let target = input.parse()?;
        input.parse::<Token![,]>()?;
        let param = input.parse()?;
        input.parse::<Token![=>]>()?;

        let block = input.call(Block::parse_within)?;
        Ok(Self {
            attrs,
            vis,
            name,
            target,
            param,
            block,
        })
    }
}

pub fn parse(input: TokenStream) -> syn::Result<TokenStream> {
    let bevy_app = Manifest::resolve_bevy("app", &input)?;
    let Syntax {
        attrs,
        vis,
        name,
        target,
        param,
        block,
    } = syn::parse2(input)?;

    let mut new_attrs = Vec::with_capacity(attrs.len());
    let mut group = false;

    for attr in attrs {
        if let Meta::Path(ref path) = attr.meta {
            if path.is_ident("plugin_group") {
                group = true;
                continue;
            }
        }

        new_attrs.push(attr);
    }

    let add_plugin = match group {
        false => quote! { builder = builder.add(plugin) },
        true => quote! { builder = builder.add_group(plugin) },
    };

    let base = quote! {
        #(#new_attrs)* #vis trait #name {
            #[doc = "Configures the plugin group builder for the types contained in this parameter type."]
            fn build(builder: #bevy_app::PluginGroupBuilder) -> #bevy_app::PluginGroupBuilder;
        }

        impl<#param: #target> #name for #param {
            #[inline]
            fn build(mut builder: #bevy_app::PluginGroupBuilder) -> #bevy_app::PluginGroupBuilder {
                let plugin = { #(#block)* };
                #add_plugin;
                builder
            }
        }
    };

    let unit = quote! {
        impl #name for () {
            #[inline]
            fn build(builder: #bevy_app::PluginGroupBuilder) -> #bevy_app::PluginGroupBuilder {
                builder
            }
        }
    };

    let tuples = (1usize..=15).fold(Vec::with_capacity(15), |mut out, end| {
        let params = (1..=end).fold(Vec::with_capacity(end), |mut params, i| {
            params.push(Ident::new(&format!("T{i}"), name.span()));
            params
        });

        let meta = if end == 1 {
            quote! {
                #[cfg_attr(any(docsrs, docsrs_dep), doc(fake_variadic))]
            }
        } else {
            quote! {
                #[doc(hidden)]
            }
        };

        out.push(quote! {
            #meta
            impl<#(#params: #name,)*> #name for (#(#params,)*) {
                fn build(mut builder: #bevy_app::PluginGroupBuilder) -> #bevy_app::PluginGroupBuilder {
                    #(builder = #params::build(builder);)*
                    builder
                }
            }
        });
        out
    });

    Ok(quote! {
        #base
        #(#tuples)*
        #unit
    })
}