use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Result};
use crate::shared::process_key;
pub(crate) fn expand(input: &DeriveInput) -> Result<TokenStream> {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let data = match &input.data {
Data::Enum(data) => data,
_ => {
return Err(syn::Error::new_spanned(
input,
"#[derive(Failure)] only supports enums",
))
},
};
let mut arms = Vec::with_capacity(data.variants.len());
for v in &data.variants {
let v_name = &v.ident;
let v_str = v_name.to_string();
let arm = match &v.fields {
Fields::Named(named) => {
let idents: Vec<&syn::Ident> = named
.named
.iter()
.map(|f| f.ident.as_ref().expect("named field"))
.collect();
let keys: Vec<String> = idents
.iter()
.map(|id| process_key(&id.to_string(), Some("CamelCase")))
.collect();
quote! {
#name::#v_name { #(#idents),* } =>
::wolfram_expr::expr!(System::Failure[#v_str, { #( #keys -> #idents ),* }]),
}
},
Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => {
quote! {
#name::#v_name(__payload) =>
::wolfram_expr::expr!(System::Failure[#v_str, { "Message" -> __payload }]),
}
},
Fields::Unit => {
quote! {
#name::#v_name => ::wolfram_expr::expr!(System::Failure[#v_str, {}]),
}
},
Fields::Unnamed(_) => {
return Err(syn::Error::new_spanned(
v,
"#[derive(Failure)] supports named-field, single-field tuple, or unit variants",
))
},
};
arms.push(arm);
}
Ok(quote! {
#[automatically_derived]
impl #impl_generics ::core::convert::From<&#name #ty_generics>
for ::wolfram_expr::Expr #where_clause
{
fn from(__value: &#name #ty_generics) -> ::wolfram_expr::Expr {
match ::core::clone::Clone::clone(__value) {
#(#arms)*
}
}
}
#[automatically_derived]
impl #impl_generics ::core::convert::From<#name #ty_generics>
for ::wolfram_expr::Expr #where_clause
{
fn from(__value: #name #ty_generics) -> ::wolfram_expr::Expr {
::wolfram_expr::Expr::from(&__value)
}
}
})
}