use proc_macro2::TokenStream;
use quote::quote;
use crate::{
Render,
core::{Attrs, Field},
params,
};
#[derive(Clone, Default)]
pub struct EnumSyntax;
impl Render for EnumSyntax {
type Args = params::EnumParams;
fn render(&self, args: Self::Args) -> syn::Result<TokenStream> {
let ident = &args.input.ident;
let (impl_generics, type_generics, where_generics) = args.input.generics.split_for_impl();
let default_variants: Vec<_> = args
.data
.variants
.iter()
.filter(|v| {
Attrs::parse(&v.attrs)
.map(|a| a.exists("default"))
.unwrap_or(false)
})
.collect();
if default_variants.is_empty() {
return Err(syn::Error::new_spanned(
&args.input.ident,
"no variant marked with #[moxy(default)]",
));
}
if default_variants.len() > 1 {
return Err(syn::Error::new_spanned(
&default_variants[1].ident,
"multiple variants marked with #[moxy(default)]",
));
}
let variant = default_variants[0];
let variant_ident = &variant.ident;
let body = match &variant.fields {
syn::Fields::Unit => {
quote! { Self::#variant_ident }
}
syn::Fields::Unnamed(_) => {
let fields: Vec<_> = variant
.fields
.iter()
.enumerate()
.map(|(i, f)| Field::parse(i, f))
.collect::<syn::Result<Vec<_>>>()?;
let defaults: Vec<_> = fields
.iter()
.map(|field| {
if let Some(value) = field.default_value() {
quote!(#value.into())
} else {
quote!(::std::default::Default::default())
}
})
.collect();
quote! { Self::#variant_ident(#(#defaults),*) }
}
syn::Fields::Named(_) => {
let fields: Vec<_> = variant
.fields
.iter()
.enumerate()
.map(|(i, f)| Field::parse(i, f))
.collect::<syn::Result<Vec<_>>>()?;
let defaults: Vec<_> = fields
.iter()
.map(|field| {
let fname = field.name();
if let Some(value) = field.default_value() {
quote!(#fname: #value.into())
} else {
quote!(#fname: ::std::default::Default::default())
}
})
.collect();
quote! { Self::#variant_ident { #(#defaults),* } }
}
};
Ok(quote! {
impl #impl_generics ::std::default::Default for #ident #type_generics #where_generics {
fn default() -> Self {
#body
}
}
})
}
}