extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{DeriveInput};
#[proc_macro_derive(DeriveIndex)]
pub fn index_enum(input: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(input as DeriveInput);
enum_traits_inner::index_enum_inner(&ast)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
mod enum_traits_inner {
use proc_macro2::{TokenStream, Span};
use syn::{DeriveInput, Data};
use quote::quote;
pub fn index_enum_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
let variants = match &ast.data {
Data::Enum(enum_data) => &enum_data.variants,
_ => return Err(syn::Error::new(Span::call_site(), "this macro only supports enums"))
};
let mut from_idxs = Vec::new();
let mut to_idxs = Vec::new();
for (count, variant) in variants.iter().enumerate() {
if !variant.fields.is_empty() {
return Err(syn::Error::new(Span::call_site(), "this macro only works on enum variants without fields"));
}
from_idxs.push(quote! {#count => Some(#name::#variant)});
to_idxs.push(quote! {#name::#variant => #count});
}
from_idxs.push(quote!(_ => None));
Ok(quote!{
#[doc = "Converts an enum to and from an index"]
impl #impl_generics EnumIndex for #name #ty_generics #where_clause {
fn from_index(idx: usize) -> Option<#name #ty_generics> where Self:Sized {
match idx {
#(#from_idxs),*
}
}
fn to_index(&self) -> usize {
match self {
#(#to_idxs),*
}
}
}
})
}
}