bk2d-macro 0.3.0

Useful macro for impl BK2D
Documentation
use darling::FromVariant;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput};

#[derive(Default, FromVariant)]
#[darling(attributes(action), default)]
struct StageAttrs {
    stage: Option<u32>,
}

macro_rules! match_arms {
    ( $x:ident, $input:expr ) => {
        match $input.data {
            Data::Enum(ref data) => data
                .variants
                .iter()
                .flat_map(|variant| m::$x(&$input.ident, variant))
                .collect(),
            _ => panic!("unsupported data structure"),
        };
    };
}

mod m {
    use super::StageAttrs;

    use darling::FromVariant;
    use proc_macro2::{Span, TokenStream};
    use quote::quote;
    use syn::{Fields, Ident, Token, Variant};

    pub fn name(name: &Ident, variant: &Variant) -> TokenStream {
        let ident = &variant.ident;
        let ident_s = ident.to_string();
        match &variant.fields {
            Fields::Unit => {
                quote! {
                    #name::#ident => #ident_s,
                }
            }
            Fields::Unnamed(fields) => {
                let bindings = vec![Token![_](Span::call_site()); fields.unnamed.len()];
                quote! {
                    #name::#ident(#(#bindings),*) => #ident_s,
                }
            }
            Fields::Named(_) => {
                quote! {
                    #name::#ident { .. } => #ident_s,
                }
            }
        }
    }

    pub fn stage(name: &Ident, variant: &Variant) -> TokenStream {
        let stage = StageAttrs::from_variant(&variant)
            .expect("failed to parse stage attributes")
            .stage
            .unwrap_or(0);
        let ident = &variant.ident;
        match &variant.fields {
            Fields::Unit => {
                quote! {
                    #name::#ident => #stage,
                }
            }
            Fields::Unnamed(fields) => {
                let bindings = vec![Token![_](Span::call_site()); fields.unnamed.len()];
                quote! {
                    #name::#ident(#(#bindings),*) => #stage,
                }
            }
            Fields::Named(_) => {
                quote! {
                    #name::#ident { .. } => #stage,
                }
            }
        }
    }
}

pub fn derive(input: DeriveInput) -> TokenStream {
    let name = &input.ident;
    let name_body: TokenStream = match_arms!(name, input);
    let stage_body: TokenStream = match_arms!(stage, input);
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    quote! {
        impl#impl_generics ::bk2d::Action for #name#ty_generics #where_clause {
            fn name(&self) -> &'static str {
                match self {
                    #name_body
                }
            }

            fn stage(&self) -> u32 {
                match self {
                    #stage_body
                }
            }
        }
    }
}