enum_index_derive 0.2.0

Macros for extracting Enum variant index
Documentation
extern crate syn;
#[macro_use]
extern crate quote;
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_derive(EnumIndex)]
pub fn enum_index(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();

    let tokens = impl_enum_index(&ast);
    tokens.parse().unwrap()
}


fn impl_enum_index(ast: &syn::DeriveInput) -> quote::Tokens {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let variants = match ast.body {
        syn::Body::Enum(ref v) => v,
        _ => panic!("EnumIndex can be only implemented for Enums")
    };

    let mut matches = Vec::new();

    for variant in variants {
        use syn::VariantData::*;
        let ident = &variant.ident;

        let params = match variant.data {
            Unit => quote::Ident::from(""),
            Tuple(..) => quote::Ident::from("(..)"),
            Struct(..) => quote::Ident::from("{..}")
        };

        let index = matches.len();
        matches.push(quote!{ #name::#ident #params => #index});
    }


    quote!{
        impl #impl_generics enum_index::EnumIndex for #name #ty_generics #where_clause {
            fn enum_index(&self) -> usize {
                match *self {
                    #(#matches),*
                }
            }
        }
    }
}


#[proc_macro_derive(IndexEnum)]
pub fn index_enum(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();

    let tokens = impl_index_enum(&ast);
    tokens.parse().unwrap()
}


fn impl_index_enum(ast: &syn::DeriveInput) -> quote::Tokens {
    let name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let variants = match ast.body {
        syn::Body::Enum(ref v) => v,
        _ => panic!("IndexEnum can be only implemented for Enums")
    };

    let mut index_matches = Vec::new();
    let mut index : usize = 0;

    for variant in variants {
        use syn::VariantData::*;
        let ident = &variant.ident;
        match variant.data {
            Unit => {
                index_matches.push(quote! { #index => Some(#name::#ident) });
            },
            Tuple(ref fields) => {
                let mut initialized_fields = Vec::new();
                for field in fields {
                    let field_type = &field.ty;
                    initialized_fields.push(quote! { #field_type::default()} );
                }
                index_matches.push(quote! {
                    #index => Some(#name::#ident(#(#initialized_fields),*))
                });
            }
            Struct(ref fields) => {
                let mut initialized_fields = Vec::new();
                for field in fields {
                    let field_name = &field.ident;
                    let field_type = &field.ty;
                    initialized_fields.push(quote! { #field_name: #field_type::default()});
                }
                index_matches.push(quote! { #index => Some(#name::#ident{#(#initialized_fields),*})});
            }
        }
        index += 1;
    }


    quote!{
        impl #impl_generics enum_index::IndexEnum for #name #ty_generics #where_clause {
            fn index_enum(index: usize) -> Option<Self> {
                match index {
                    #(#index_matches),*,
                    _ => None,
                }
            }
        }
    }
}