use proc_macro2::{Literal, Span};
use quote::quote;
use syn::{Data, Fields, Ident};
pub fn derive(input: syn::DeriveInput) -> proc_macro2::TokenStream {
let enum_name = &input.ident;
let data = match &input.data {
Data::Enum(e) => e,
_ => panic!("Cannot derive for non-enum type"),
};
let mut i = 0u32;
let cases = data.variants.iter()
.map(|variant| {
let ident = &variant.ident;
let variant_name = Literal::string(&ident.to_string());
let fields = match &variant.fields {
Fields::Named(fields) => {
let mapped = fields.named.iter().map(|field| {
let name = match &field.ident {
Some(name) => name,
None => panic!("Named fields should have names"),
};
let ty = &field.ty;
quote!( #name: #ty::default() )
});
quote!( { #(#mapped),* } )
},
Fields::Unnamed(fields) => {
let mapped = fields.unnamed.iter().map(|field| &field.ty );
quote!{ ( #(#mapped::default()),* ) }
},
Fields::Unit => quote!(),
};
i += 1;
let ord = Literal::u32_suffixed(i);
quote! { #ord => Some((#variant_name, &|| #enum_name::#ident #fields)) }
});
let iter_name = Ident::new(&format!("{}AllIter", enum_name.to_string()), Span::call_site());
quote! {
impl ::derive_enum::Iter for #enum_name {
fn all() -> Box<dyn Iterator<Item = (&'static str, &'static dyn Fn() -> Self)>> { Box::new(#iter_name(0)) }
}
struct #iter_name(u32);
impl ::std::iter::Iterator for #iter_name {
type Item = (&'static str, &'static dyn Fn() -> #enum_name);
fn next(&mut self) -> Option<Self::Item> {
self.0 += 1;
match self.0 {
#(#cases,)*
_ => None,
}
}
}
}
}