derive_custom_enum_traits/
lib.rs1extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use syn::{DeriveInput};
7
8#[proc_macro_derive(DeriveIndex)]
9pub fn index_enum(input: TokenStream) -> TokenStream {
44 let ast = syn::parse_macro_input!(input as DeriveInput);
45 enum_traits_inner::index_enum_inner(&ast)
46 .unwrap_or_else(syn::Error::into_compile_error)
47 .into()
48}
49
50mod enum_traits_inner {
51 use proc_macro2::{TokenStream, Span};
52 use syn::{DeriveInput, Data};
53 use quote::quote;
54
55 pub fn index_enum_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
56 let name = &ast.ident;
57 let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl();
58 let variants = match &ast.data {
59 Data::Enum(enum_data) => &enum_data.variants,
60 _ => return Err(syn::Error::new(Span::call_site(), "this macro only supports enums"))
61 };
62 let mut from_idxs = Vec::new();
63 let mut to_idxs = Vec::new();
64 for (count, variant) in variants.iter().enumerate() {
65 if !variant.fields.is_empty() {
66 return Err(syn::Error::new(Span::call_site(), "this macro only works on enum variants without fields"));
67 }
68 from_idxs.push(quote! {#count => Some(#name::#variant)});
69 to_idxs.push(quote! {#name::#variant => #count});
70 }
71 from_idxs.push(quote!(_ => None));
72 Ok(quote!{
73 #[doc = "Converts an enum to and from an index"]
74 impl #impl_generics EnumIndex for #name #ty_generics #where_clause {
75 fn from_index(idx: usize) -> Option<#name #ty_generics> where Self:Sized {
76 match idx {
77 #(#from_idxs),*
78 }
79 }
80
81 fn to_index(&self) -> usize {
82 match self {
83 #(#to_idxs),*
84 }
85 }
86 }
87 })
88 }
89}