actionable-macros 0.1.0-dev.2

Macros for `actionable`
Documentation
#![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, ()>,

    /// Overrides the crate name for `actionable` references.
    #[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())
}