#![doc = include_str!("../README.md")]
use proc_macro::TokenStream;
use syn::{Data, DeriveInput, Expr, ExprLit, Lit, parse_macro_input};
use quote::quote;
#[proc_macro_derive(EnumValues)]
pub fn generate_enum_values_impl(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let Data::Enum(data_enum) = &input.data else {
return syn::Error::new_spanned(name, "EnumReflect only works on enums")
.to_compile_error()
.into();
};
let get_fields_arms = data_enum
.variants
.iter()
.map(|v| {
let variant_ident = &v.ident;
let doc = v
.attrs
.iter()
.filter(|a| a.path().is_ident("doc"))
.filter_map(|a| {
let expr = if let Ok(nv) = &a.meta.require_name_value() {
&nv.value
} else {
return None;
};
if let Expr::Lit(ExprLit { lit, .. }) = expr
&& let Lit::Str(str) = lit
{
return Some(str.value().clone());
}
None
})
.collect::<String>();
let Some(discriminant) = v.discriminant.as_ref() else {
return syn::Error::new_spanned(name, "No discriminant for enum variant")
.to_compile_error();
};
let value = if let Expr::Lit(lit) = &discriminant.1 {
if let Lit::Int(int) = &lit.lit {
if let Ok(value) = int.base10_parse::<u16>() {
value
} else {
return syn::Error::new_spanned(name, "Cannot parse int literal as u16")
.to_compile_error();
}
} else {
return syn::Error::new_spanned(name, "Cannot parse int literal")
.to_compile_error();
}
} else {
return syn::Error::new_spanned(name, "Enum discriminant should be a literal")
.to_compile_error();
};
quote! { enum_values::VariantInfo {
name: stringify!(#variant_ident),
value: #value,
doc: #doc.trim()
} }
})
.collect::<Vec<_>>();
quote! {
impl enum_values::EnumValues for #name {
fn variants() -> impl Iterator<Item = enum_values::VariantInfo> {
[ #(#get_fields_arms),* ].into_iter()
}
}
}
.into()
}