use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, parse_macro_input};
#[proc_macro_derive(AccessLevel)]
pub fn derive_access_level(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let variants = match &input.data {
Data::Enum(data_enum) => &data_enum.variants,
_ => {
return syn::Error::new_spanned(&input, "AccessLevel can only be derived for enums")
.to_compile_error()
.into();
}
};
for variant in variants {
if !matches!(variant.fields, Fields::Unit) {
return syn::Error::new_spanned(
variant,
"AccessLevel can only be derived for enums with unit variants (no fields)",
)
.to_compile_error()
.into();
}
}
let from_str_arms = variants.iter().map(|variant| {
let variant_name = &variant.ident;
let variant_str = variant_name.to_string();
quote! {
#variant_str => Some(Self::#variant_name)
}
});
let as_str_arms = variants.iter().map(|variant| {
let variant_name = &variant.ident;
let variant_str = variant_name.to_string();
quote! {
Self::#variant_name => #variant_str
}
});
let expanded = quote! {
#[automatically_derived]
impl ::nut_shell::auth::AccessLevel for #name {
fn from_str(s: &str) -> Option<Self> {
match s {
#(#from_str_arms,)*
_ => None,
}
}
fn as_str(&self) -> &'static str {
match self {
#(#as_str_arms,)*
}
}
}
};
TokenStream::from(expanded)
}