use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Variant};
pub fn enum_factory(args: TokenStream, input: TokenStream) -> TokenStream {
let builder: Ident =
syn::parse(args).expect("expceted a builder, e.g. #[enum_factory(Builder)]");
let derive_input: DeriveInput = parse_macro_input!(input);
let derive_input_copied = derive_input.clone();
let ident = derive_input.ident;
let data_enum = match derive_input.data {
Data::Enum(data_enum) => data_enum,
_ => panic!("enum_factory can only be derived for enums"),
};
let match_arms: Vec<_> = data_enum
.variants
.iter()
.map(|v| {
let Variant { ident: variant_name, fields, attrs, .. } = v;
match fields {
Fields::Unnamed(fields_unnamed) if fields_unnamed.unnamed.len() == 1 => {
let variant_s = variant_name.to_string();
quote! {
#(#attrs)*
#variant_s => {
builder.build(|inner| Ok(Self::#variant_name(inner)))
}
}
}
_ => panic!("variant `{}` is not a single tuple variant", variant_name),
}
})
.collect();
TokenStream::from(quote! {
#derive_input_copied
impl #ident {
pub fn new(kind: &str, builder: #builder) -> anyhow::Result<Self> {
match kind {
#(#match_arms)*
other => anyhow::bail!("unknown enum variant: {}", other),
}
}
}
})
}