enum_index_derive/
lib.rs

1extern crate syn;
2#[macro_use]
3extern crate quote;
4extern crate proc_macro;
5use proc_macro::TokenStream;
6
7#[proc_macro_derive(EnumIndex)]
8pub fn enum_index(input: TokenStream) -> TokenStream {
9    let s = input.to_string();
10    let ast = syn::parse_derive_input(&s).unwrap();
11
12    let tokens = impl_enum_index(&ast);
13    tokens.parse().unwrap()
14}
15
16
17fn impl_enum_index(ast: &syn::DeriveInput) -> quote::Tokens {
18    let name = &ast.ident;
19    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
20
21    let variants = match ast.body {
22        syn::Body::Enum(ref v) => v,
23        _ => panic!("EnumIndex can be only implemented for Enums")
24    };
25
26    let mut matches = Vec::new();
27
28    for variant in variants {
29        use syn::VariantData::*;
30        let ident = &variant.ident;
31
32        let params = match variant.data {
33            Unit => quote::Ident::from(""),
34            Tuple(..) => quote::Ident::from("(..)"),
35            Struct(..) => quote::Ident::from("{..}")
36        };
37
38        let index = matches.len();
39        matches.push(quote!{ #name::#ident #params => #index});
40    }
41
42
43    quote!{
44        impl #impl_generics enum_index::EnumIndex for #name #ty_generics #where_clause {
45            fn enum_index(&self) -> usize {
46                match *self {
47                    #(#matches),*
48                }
49            }
50        }
51    }
52}
53
54
55#[proc_macro_derive(IndexEnum)]
56pub fn index_enum(input: TokenStream) -> TokenStream {
57    let s = input.to_string();
58    let ast = syn::parse_derive_input(&s).unwrap();
59
60    let tokens = impl_index_enum(&ast);
61    tokens.parse().unwrap()
62}
63
64
65fn impl_index_enum(ast: &syn::DeriveInput) -> quote::Tokens {
66    let name = &ast.ident;
67    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
68
69    let variants = match ast.body {
70        syn::Body::Enum(ref v) => v,
71        _ => panic!("IndexEnum can be only implemented for Enums")
72    };
73
74    let mut index_matches = Vec::new();
75    let mut index : usize = 0;
76
77    for variant in variants {
78        use syn::VariantData::*;
79        let ident = &variant.ident;
80        match variant.data {
81            Unit => {
82                index_matches.push(quote! { #index => Some(#name::#ident) });
83            },
84            Tuple(ref fields) => {
85                let mut initialized_fields = Vec::new();
86                for field in fields {
87                    let field_type = &field.ty;
88                    initialized_fields.push(quote! { #field_type::default()} );
89                }
90                index_matches.push(quote! {
91                    #index => Some(#name::#ident(#(#initialized_fields),*))
92                });
93            }
94            Struct(ref fields) => {
95                let mut initialized_fields = Vec::new();
96                for field in fields {
97                    let field_name = &field.ident;
98                    let field_type = &field.ty;
99                    initialized_fields.push(quote! { #field_name: #field_type::default()});
100                }
101                index_matches.push(quote! { #index => Some(#name::#ident{#(#initialized_fields),*})});
102            }
103        }
104        index += 1;
105    }
106
107
108    quote!{
109        impl #impl_generics enum_index::IndexEnum for #name #ty_generics #where_clause {
110            fn index_enum(index: usize) -> Option<Self> {
111                match index {
112                    #(#index_matches),*,
113                    _ => None,
114                }
115            }
116        }
117    }
118}