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}