use quote::quote;
use syn::{FieldsNamed, FieldsUnnamed, ItemEnum, Variant};
use syn::__private::TokenStream2;
use syn::Fields::*;
use crate::enum_error::VariantWithParams;
use crate::impl_from::FromImplementationError;
use crate::impl_from::FromImplementationError::{EnumNotExactlyOneField, ParameterOnEnumAndVariant};
use crate::parameters::{IMPL_FROM, Parameters};
pub struct EnumFromImplementer<'a> {
item_enum: &'a ItemEnum,
enum_parameters: &'a Parameters,
variants_with_parameters: &'a Vec<VariantWithParams<'a>>,
}
impl<'a> EnumFromImplementer<'a> {
pub fn new(item_enum: &'a ItemEnum, enum_parameters: &'a Parameters, variants_with_parameters: &'a Vec<VariantWithParams<'a>>) -> Self {
EnumFromImplementer { item_enum, enum_parameters, variants_with_parameters }
}
pub fn implement(self) -> Result<TokenStream2, FromImplementationError> {
let global_implement = self.enum_parameters.bool_for_name(IMPL_FROM);
let impl_from_variants = self.get_impl_from_variants();
self.validate_impl_from_settings(global_implement, &impl_from_variants)?;
let implementations = match global_implement {
true => self.implement_for_variants(self.item_enum.variants.iter()),
false => self.implement_for_variants(impl_from_variants)
};
Ok(quote! {#(#implementations)*})
}
fn get_impl_from_variants(&self) -> Vec<&Variant> {
self.variants_with_parameters
.iter()
.filter_map(|(v, p_opt)| match p_opt {
Some(p) => Some((v, p)),
_ => None
})
.filter_map(|(v, p)| match p.bool_for_name(IMPL_FROM) {
true => Some(*v),
false => None
})
.collect()
}
fn validate_impl_from_settings(&self, global_impl_from: bool, impl_from_variants: &Vec<&Variant>) -> Result<(), FromImplementationError> {
if global_impl_from && impl_from_variants.len() > 0 {
return Err(ParameterOnEnumAndVariant(self.item_enum.ident.clone()));
}
let variant_idents_with_not_one_field = match global_impl_from {
true => self.item_enum.variants.iter()
.filter(|v| self.variant_num_fields(v) != 1)
.map(|v| v.ident.clone())
.collect::<Vec<_>>(),
false => impl_from_variants.iter()
.filter(|v| self.variant_num_fields(v) != 1)
.map(|v| v.ident.clone())
.collect::<Vec<_>>()
};
match variant_idents_with_not_one_field.len() {
0 => Ok(()),
_ => Err(EnumNotExactlyOneField(self.item_enum.ident.clone(), variant_idents_with_not_one_field))
}
}
fn variant_num_fields(&self, variant: &Variant) -> usize {
match &variant.fields {
Named(f) => f.named.len(),
Unnamed(f) => f.unnamed.len(),
Unit => 0
}
}
fn implement_for_variants<'b, I>(&self, variants: I) -> Vec<TokenStream2>
where I: IntoIterator<Item=&'b Variant> {
variants.into_iter()
.map(|v| self.implement_for_variant(v))
.collect()
}
fn implement_for_variant(&self, variant: &Variant) -> TokenStream2 {
match &variant.fields {
Named(ref fields) => self.implement_for_named(variant, fields),
Unnamed(ref fields) => self.implement_for_unnamed(variant, fields),
Unit => unreachable!()
}
}
fn implement_for_named(&self, variant: &Variant, fields: &FieldsNamed) -> TokenStream2 {
let enum_ident = &self.item_enum.ident;
let generics = &self.item_enum.generics;
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let variant_ident = &variant.ident;
let field = fields.named.first().unwrap();
let ty = &field.ty;
let field_ident = field.ident.as_ref().unwrap();
quote! {
impl #impl_generics std::convert::From<#ty> for #enum_ident #type_generics #where_clause {
fn from(val: #ty) -> Self {
#enum_ident::#variant_ident{ #field_ident : val }
}
}
}
}
fn implement_for_unnamed(&self, variant: &Variant, fields: &FieldsUnnamed) -> TokenStream2 {
let enum_ident = &self.item_enum.ident;
let generics = &self.item_enum.generics;
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let variant_ident = &variant.ident;
let field = fields.unnamed.first().unwrap();
let ty = &field.ty;
quote! {
impl #impl_generics std::convert::From<#ty> for #enum_ident #type_generics #where_clause {
fn from(val: #ty) -> Self {
#enum_ident::#variant_ident(val)
}
}
}
}
}