1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#![recursion_limit = "128"] extern crate proc_macro; extern crate syn; #[macro_use] extern crate quote; use std::iter; use proc_macro::TokenStream; use syn::{Body, Ident, Variant, VariantData}; use quote::Tokens; fn generate_enum_code(name: &Ident, variants: &[Variant]) -> Tokens { let mut enum_count = 0usize; for &Variant { ref data, ref discriminant, .. } in variants { if data != &VariantData::Unit { panic!("#[derive(EnumMap)] requires C style style enum"); } if discriminant.is_some() { panic!("#[derive(EnumMap)] doesn't currently support discriminants"); } enum_count += 1; } let variant_a = variants.iter().map(|variant| &variant.ident); let variant_b = variants.iter().map(|variant| &variant.ident); let repeat_name_a = iter::repeat(name).take(variants.len()); let repeat_name_b = repeat_name_a.clone(); let counter = 0..variants.len(); quote! { impl<V> ::enum_map::Internal<V> for #name { type Array = [V; #enum_count]; fn slice(array: &Self::Array) -> &[V] { array } fn slice_mut(array: &mut Self::Array) -> &mut [V] { array } fn from_usize(value: usize) -> Self { match value { #( #counter => #repeat_name_a::#variant_a, )* _ => unreachable!() } } fn to_usize(self) -> usize { self as usize } fn from_function<F: Fn(Self) -> V>(f: F) -> Self::Array { [#( f(#repeat_name_b::#variant_b), )*] } } } } #[proc_macro_derive(EnumMap)] pub fn derive_enum_map(input: TokenStream) -> TokenStream { let input = syn::parse_macro_input(&input.to_string()).unwrap(); match input.body { Body::Enum(ref variants) => generate_enum_code(&input.ident, variants), _ => panic!("#[derive(EnumMap)] is only defined for enums"), } .parse() .unwrap() }