enum-helper-derive 0.1.0

Procedural macro implementation for enum-helper
Documentation
use proc_macro2::TokenStream;
use quote::quote;

use crate::enum_kind::Ir;

pub fn generate(ir: Ir<'_>) -> TokenStream {
    let mut blocks = Vec::new();

    blocks.push(gen_kind_struct(&ir));
    blocks.push(gen_impl_enum_kind(&ir));

    quote! {
        #(#blocks)*
    }
}

fn gen_kind_struct(ir: &Ir<'_>) -> TokenStream {
    let vis = ir.vis;
    let kind_ident = &ir.kind_ident;

    let attrs = &ir.attrs;

    let default_derive = if ir.default_derive {
        quote! {
            #[derive(
                ::core::fmt::Debug,
                ::core::clone::Clone,
                ::core::marker::Copy,
                ::core::cmp::PartialEq,
                ::core::cmp::Eq,
            )]
        }
    } else {
        quote! {}
    };

    let variants: Vec<_> = ir
        .variants
        .iter()
        .map(|v| {
            let v_name = &v.kind_name;
            let attrs = &v.attrs;
            quote! {
                #(#[#attrs])*
                #v_name,
            }
        })
        .collect();

    quote! {
        #[automatically_derived]
        #default_derive
        #(#[#attrs])*
        #vis enum #kind_ident {
            #(#variants)*
        }
    }
}

fn gen_impl_enum_kind(ir: &Ir<'_>) -> TokenStream {
    let ident = &ir.ident;
    let kind_ident = &ir.kind_ident;
    let (impl_generics, ty_generics, where_clause) = &ir.generics.split_for_impl();

    let arms: Vec<_> = ir
        .variants
        .iter()
        .map(|v| {
            let v_ident = &v.ident;
            let kind_name = &v.kind_name;
            let lhs = match v.fields {
                syn::Fields::Named(_) => quote! { Self::#v_ident {..} },
                syn::Fields::Unnamed(_) => quote! { Self::#v_ident(..) },
                syn::Fields::Unit => quote! { Self::#v_ident },
            };
            quote! { #lhs => #kind_ident::#kind_name }
        })
        .collect();

    quote! {
        #[automatically_derived]
        impl #impl_generics ::enum_helper::EnumKind for #ident #ty_generics #where_clause {
            type Kind = #kind_ident;
            fn kind(&self) -> <Self as ::enum_helper::EnumKind>::Kind {
                match self {
                    #(#arms,)*
                }
            }
        }
    }
}