use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{Data, DataEnum, DeriveInput, parse_macro_input};
#[proc_macro_attribute]
pub fn rawenum(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let name = &input.ident;
let Data::Enum(DataEnum { variants, .. }) = &input.data else {
return syn::Error::new_spanned(input, "rawenum can only be applied to enums")
.to_compile_error()
.into();
};
let integer_types = vec![
("i8", quote! { i8 }),
("u8", quote! { u8 }),
("i16", quote! { i16 }),
("u16", quote! { u16 }),
("i32", quote! { i32 }),
("u32", quote! { u32 }),
("i64", quote! { i64 }),
("u64", quote! { u64 }),
];
let mut all_generated_methods = Vec::new();
for (type_str, type_tokens) in integer_types.iter() {
let fn_name = format_ident!("from_{}", type_str);
let mut local_generated_consts = Vec::new();
let mut local_match_arms = Vec::new();
for variant in variants {
let variant_name = &variant.ident;
let const_name = format_ident!(
"__RAWENUM_{}_DISCRIMINANT_{}_{}",
name.to_string().to_uppercase(),
variant_name.to_string().to_uppercase(),
type_str.to_uppercase()
);
local_generated_consts.push(quote! {
const #const_name: #type_tokens = #name::#variant_name as #type_tokens;
});
local_match_arms.push(quote! {
#const_name => Some(Self::#variant_name),
});
}
local_match_arms.push(quote! {
_ => None,
});
let method_code = quote! {
#[allow(dead_code)] pub fn #fn_name(value: #type_tokens) -> Option<Self> {
#( #local_generated_consts )*
match value {
#( #local_match_arms )*
}
}
};
all_generated_methods.push(method_code);
}
let expanded = quote! {
#input
impl #name {
#( #all_generated_methods )* }
};
expanded.into()
}