#![allow(clippy::default_trait_access)]
use darling::{ast, FromDeriveInput, FromField, FromVariant, ToTokens};
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use quote::quote;
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(action), supports(enum_any))]
struct Action {
ident: syn::Ident,
vis: syn::Visibility,
data: ast::Data<Variant, ()>,
#[darling(default)]
actionable: Option<String>,
}
#[derive(Debug, FromVariant)]
struct Variant {
ident: syn::Ident,
fields: ast::Fields<Field>,
}
#[derive(Debug, FromField)]
struct Field {
ident: Option<syn::Ident>,
ty: syn::Type,
}
impl ToTokens for Action {
fn to_tokens(&self, tokens: &mut TokenStream) {
let name = &self.ident;
let enum_data = self
.data
.as_ref()
.take_enum()
.expect("Expected enum in data");
let actionable = self.actionable.as_deref().unwrap_or("actionable");
let actionable = syn::Ident::new(actionable, name.span());
let variants = enum_data.into_iter().map(|variant| {
let ident = variant.ident.clone();
let ident_as_string = ident.to_string();
match variant.fields.len() {
0 => {
quote! {
Self::#ident => #actionable::ActionName(vec![::std::borrow::Cow::Borrowed(#ident_as_string)])
}
}
1 => {
quote! {
Self::#ident(subaction) => {
let mut name = Action::name(subaction);
name.0.insert(0, ::std::borrow::Cow::Borrowed(#ident_as_string));
name
}
}
}
_ => {
abort!(
variant.ident,
"For derive(Action), all enum variants may have at most 1 field"
)
}
}
});
tokens.extend(quote! {
impl Action for #name {
fn name(&self) -> #actionable::ActionName {
match self {
#(
#variants
),*
}
}
}
})
}
}
pub fn derive(input: &syn::DeriveInput) -> Result<TokenStream, darling::Error> {
let actionable = Action::from_derive_input(input)?;
Ok(actionable.into_token_stream())
}