error_generator/impl_from/
enums.rs

1use quote::quote;
2use syn::{FieldsNamed, FieldsUnnamed, ItemEnum, Variant};
3use syn::__private::TokenStream2;
4use syn::Fields::*;
5
6use crate::enum_error::VariantWithParams;
7use crate::impl_from::FromImplementationError;
8use crate::impl_from::FromImplementationError::{EnumNotExactlyOneField, ParameterOnEnumAndVariant};
9use crate::parameters::{IMPL_FROM, Parameters};
10
11pub struct EnumFromImplementer<'a> {
12    item_enum: &'a ItemEnum,
13    enum_parameters: &'a Parameters,
14    variants_with_parameters: &'a Vec<VariantWithParams<'a>>,
15}
16
17impl<'a> EnumFromImplementer<'a> {
18    pub fn new(item_enum: &'a ItemEnum, enum_parameters: &'a Parameters, variants_with_parameters: &'a Vec<VariantWithParams<'a>>) -> Self {
19        EnumFromImplementer { item_enum, enum_parameters, variants_with_parameters }
20    }
21
22    /// Creates std::convert::From implementations for every enum variant where
23    /// From should be implemented (based on parameters).
24    ///
25    /// The implementations for every variant are merged into a single token stream. If anything fails,
26    /// a Vec of errors is returned (even if some implementations could be created).
27    ///
28    /// An error might occur if
29    ///     a variant does not have exactly one field
30    ///     the enum and one variant are both marked with the parameter 'impl_from'
31    pub fn implement(self) -> Result<TokenStream2, FromImplementationError> {
32        let global_implement = self.enum_parameters.bool_for_name(IMPL_FROM);
33        let impl_from_variants = self.get_impl_from_variants();
34
35        self.validate_impl_from_settings(global_implement, &impl_from_variants)?;
36
37        let implementations = match global_implement {
38            true => self.implement_for_variants(self.item_enum.variants.iter()),
39            false => self.implement_for_variants(impl_from_variants)
40        };
41
42        Ok(quote! {#(#implementations)*})
43    }
44
45    /// Return all variants with set IMPL_FROM parameter
46    fn get_impl_from_variants(&self) -> Vec<&Variant> {
47        self.variants_with_parameters
48            .iter()
49            .filter_map(|(v, p_opt)| match p_opt {
50                Some(p) => Some((v, p)),
51                _ => None
52            })
53            .filter_map(|(v, p)| match p.bool_for_name(IMPL_FROM) {
54                true => Some(*v),
55                false => None
56            })
57            .collect()
58    }
59
60    /// Check the global IMPL_FROM setting and all variants with impl_from set.
61    ///
62    /// An error occurs if
63    /// the global IMPL_FROM was set and at least one variant has impl_from set
64    /// OR
65    /// variants with IMPL_FROM set have not exactly one field.
66    ///
67    /// If the global IMPL_FROM is
68    ///     true, all variants are checked
69    ///     false, only variants with IMPL_FROM set are checked
70    fn validate_impl_from_settings(&self, global_impl_from: bool, impl_from_variants: &Vec<&Variant>) -> Result<(), FromImplementationError> {
71        if global_impl_from && impl_from_variants.len() > 0 {
72            return Err(ParameterOnEnumAndVariant(self.item_enum.ident.clone()));
73        }
74
75        let variant_idents_with_not_one_field = match global_impl_from {
76            true => self.item_enum.variants.iter()
77                .filter(|v| self.variant_num_fields(v) != 1)
78                .map(|v| v.ident.clone())
79                .collect::<Vec<_>>(),
80            false => impl_from_variants.iter()
81                .filter(|v| self.variant_num_fields(v) != 1)
82                .map(|v| v.ident.clone())
83                .collect::<Vec<_>>()
84        };
85
86        match variant_idents_with_not_one_field.len() {
87            0 => Ok(()),
88            _ => Err(EnumNotExactlyOneField(self.item_enum.ident.clone(), variant_idents_with_not_one_field))
89        }
90    }
91
92    fn variant_num_fields(&self, variant: &Variant) -> usize {
93        match &variant.fields {
94            Named(f) => f.named.len(),
95            Unnamed(f) => f.unnamed.len(),
96            Unit => 0
97        }
98    }
99
100    fn implement_for_variants<'b, I>(&self, variants: I) -> Vec<TokenStream2>
101        where I: IntoIterator<Item=&'b Variant> {
102        variants.into_iter()
103            .map(|v| self.implement_for_variant(v))
104            .collect()
105    }
106
107    fn implement_for_variant(&self, variant: &Variant) -> TokenStream2 {
108        match &variant.fields {
109            Named(ref fields) => self.implement_for_named(variant, fields),
110            Unnamed(ref fields) => self.implement_for_unnamed(variant, fields),
111            Unit => unreachable!()
112        }
113    }
114
115    fn implement_for_named(&self, variant: &Variant, fields: &FieldsNamed) -> TokenStream2 {
116        let enum_ident = &self.item_enum.ident;
117        let generics = &self.item_enum.generics;
118        let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
119        let variant_ident = &variant.ident;
120        let field = fields.named.first().unwrap();
121        let ty = &field.ty;
122        let field_ident = field.ident.as_ref().unwrap();
123
124        quote! {
125            impl #impl_generics std::convert::From<#ty> for #enum_ident #type_generics #where_clause {
126                fn from(val: #ty) -> Self {
127                    #enum_ident::#variant_ident{ #field_ident : val }
128                }
129            }
130        }
131    }
132
133    fn implement_for_unnamed(&self, variant: &Variant, fields: &FieldsUnnamed) -> TokenStream2 {
134        let enum_ident = &self.item_enum.ident;
135        let generics = &self.item_enum.generics;
136        let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
137        let variant_ident = &variant.ident;
138        let field = fields.unnamed.first().unwrap();
139        let ty = &field.ty;
140
141        quote! {
142            impl #impl_generics std::convert::From<#ty> for #enum_ident #type_generics #where_clause {
143                fn from(val: #ty) -> Self {
144                    #enum_ident::#variant_ident(val)
145                }
146            }
147        }
148    }
149}