use proc_macro::TokenStream;
use syn::{spanned::Spanned, Data};

/// Macro to allow Recordable to be implemented on an enum by dispatching over each variant.
#[proc_macro_derive(Recordable)]
pub fn extract(input: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(input);

    impl_recordable(&ast)
}

fn impl_recordable(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;

    match &ast.data {
        Data::Enum(en) => {
            let extracted = en.variants.iter().map(|var| {
                let ident = &var.ident;

                quote::quote_spanned! {var.span()=>
                    Self::#ident (opt) => opt.run(ctx).await?
                }
            });

            TokenStream::from(quote::quote! {
                impl Recordable for #name {
                    async fn run(self, ctx: crate::Context<'_>) -> Result<(), crate::Error> {
                        match self {
                            #(#extracted,)*
                        }

                        Ok(())
                    }
                }
            })
        }

        _ => {
            panic!("Only enums can derive Recordable");
        }
    }
}
