educe/trait_handlers/clone/
clone_enum.rs

1use quote::{format_ident, quote};
2use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Meta, Type, Variant};
3
4use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder};
5use crate::{
6    common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler,
7};
8
9pub(crate) struct CloneEnumHandler;
10
11impl TraitHandler for CloneEnumHandler {
12    #[inline]
13    fn trait_meta_handler(
14        ast: &DeriveInput,
15        token_stream: &mut proc_macro2::TokenStream,
16        traits: &[Trait],
17        meta: &Meta,
18    ) -> syn::Result<()> {
19        let type_attribute = TypeAttributeBuilder {
20            enable_flag: true, enable_bound: true
21        }
22        .build_from_clone_meta(meta)?;
23
24        let mut bound: WherePredicates = Punctuated::new();
25
26        let mut clone_token_stream = proc_macro2::TokenStream::new();
27        let mut clone_from_token_stream = proc_macro2::TokenStream::new();
28
29        if let Data::Enum(data) = &ast.data {
30            type Variants<'a> = Vec<(&'a Variant, Vec<(&'a Field, FieldAttribute)>)>;
31
32            let mut variants: Variants = Vec::new();
33
34            #[cfg(feature = "Copy")]
35            let mut has_custom_clone_method = false;
36
37            for variant in data.variants.iter() {
38                let _ = TypeAttributeBuilder {
39                    enable_flag: false, enable_bound: false
40                }
41                .build_from_attributes(&variant.attrs, traits)?;
42
43                let mut variant_fields: Vec<(&Field, FieldAttribute)> = Vec::new();
44
45                for field in variant.fields.iter() {
46                    let field_attribute = FieldAttributeBuilder {
47                        enable_method: true
48                    }
49                    .build_from_attributes(&field.attrs, traits)?;
50
51                    #[cfg(feature = "Copy")]
52                    if field_attribute.method.is_some() {
53                        has_custom_clone_method = true;
54                    }
55
56                    variant_fields.push((field, field_attribute));
57                }
58
59                variants.push((variant, variant_fields));
60            }
61
62            #[cfg(feature = "Copy")]
63            let contains_copy = !has_custom_clone_method && traits.contains(&Trait::Copy);
64
65            #[cfg(not(feature = "Copy"))]
66            let contains_copy = false;
67
68            if contains_copy {
69                clone_token_stream.extend(quote!(*self));
70            }
71
72            let mut clone_types: Vec<&Type> = Vec::new();
73
74            if variants.is_empty() {
75                if !contains_copy {
76                    clone_token_stream.extend(quote!(unreachable!()));
77                    clone_from_token_stream.extend(quote!(let _ = source;));
78                }
79            } else {
80                let mut clone_variants_token_stream = proc_macro2::TokenStream::new();
81                let mut clone_from_variants_token_stream = proc_macro2::TokenStream::new();
82
83                for (variant, variant_fields) in variants {
84                    let variant_ident = &variant.ident;
85
86                    match &variant.fields {
87                        Fields::Unit => {
88                            clone_variants_token_stream.extend(quote! {
89                                Self::#variant_ident => Self::#variant_ident,
90                            });
91                            clone_from_variants_token_stream.extend(quote! {
92                                Self::#variant_ident => {
93                                    if let Self::#variant_ident = source {
94                                        // same
95                                    } else {
96                                        *self = ::core::clone::Clone::clone(source);
97                                    }
98                                },
99                            });
100                        },
101                        Fields::Named(_) => {
102                            let mut pattern_src_token_stream = proc_macro2::TokenStream::new();
103                            let mut pattern_dst_token_stream = proc_macro2::TokenStream::new();
104                            let mut cl_fields_token_stream = proc_macro2::TokenStream::new();
105                            let mut cf_body_token_stream = proc_macro2::TokenStream::new();
106
107                            for (field, field_attribute) in variant_fields {
108                                let field_name_real = field.ident.as_ref().unwrap();
109                                let field_name_src = format_ident!("_s_{}", field_name_real);
110                                let field_name_dst = format_ident!("_d_{}", field_name_real);
111
112                                pattern_src_token_stream
113                                    .extend(quote!(#field_name_real: #field_name_src,));
114                                pattern_dst_token_stream
115                                    .extend(quote!(#field_name_real: #field_name_dst,));
116
117                                if let Some(clone) = field_attribute.method.as_ref() {
118                                    cl_fields_token_stream.extend(quote! {
119                                        #field_name_real: #clone(#field_name_src),
120                                    });
121                                    cf_body_token_stream.extend(
122                                        quote!(*#field_name_dst = #clone(#field_name_src);),
123                                    );
124                                } else {
125                                    clone_types.push(&field.ty);
126
127                                    cl_fields_token_stream.extend(quote! {
128                                        #field_name_real: ::core::clone::Clone::clone(#field_name_src),
129                                    });
130                                    cf_body_token_stream.extend(
131                                        quote!( ::core::clone::Clone::clone_from(#field_name_dst, #field_name_src); ),
132                                    );
133                                }
134                            }
135
136                            clone_variants_token_stream.extend(quote! {
137                                    Self::#variant_ident { #pattern_src_token_stream } => Self::#variant_ident { #cl_fields_token_stream },
138                                });
139
140                            clone_from_variants_token_stream.extend(quote! {
141                                    Self::#variant_ident { #pattern_dst_token_stream } => {
142                                        if let Self::#variant_ident { #pattern_src_token_stream } = source {
143                                            #cf_body_token_stream
144                                        } else {
145                                            *self = ::core::clone::Clone::clone(source);
146                                        }
147                                    },
148                                });
149                        },
150                        Fields::Unnamed(_) => {
151                            let mut pattern_token_stream = proc_macro2::TokenStream::new();
152                            let mut pattern2_token_stream = proc_macro2::TokenStream::new();
153                            let mut fields_token_stream = proc_macro2::TokenStream::new();
154                            let mut body_token_stream = proc_macro2::TokenStream::new();
155
156                            for (index, (field, field_attribute)) in
157                                variant_fields.into_iter().enumerate()
158                            {
159                                let field_name_src = format_ident!("_{}", index);
160
161                                pattern_token_stream.extend(quote!(#field_name_src,));
162
163                                let field_name_dst = format_ident!("_{}", field_name_src);
164
165                                pattern2_token_stream.extend(quote!(#field_name_dst,));
166
167                                if let Some(clone) = field_attribute.method.as_ref() {
168                                    fields_token_stream.extend(quote! (#clone(#field_name_src),));
169                                    body_token_stream.extend(
170                                        quote!(*#field_name_src = #clone(#field_name_dst);),
171                                    );
172                                } else {
173                                    clone_types.push(&field.ty);
174
175                                    fields_token_stream.extend(
176                                        quote! ( ::core::clone::Clone::clone(#field_name_src), ),
177                                    );
178                                    body_token_stream.extend(
179                                        quote!( ::core::clone::Clone::clone_from(#field_name_src, #field_name_dst); ),
180                                    );
181                                }
182                            }
183
184                            clone_variants_token_stream.extend(quote! {
185                                    Self::#variant_ident ( #pattern_token_stream ) => Self::#variant_ident ( #fields_token_stream ),
186                                });
187
188                            clone_from_variants_token_stream.extend(quote! {
189                                    Self::#variant_ident ( #pattern_token_stream ) => {
190                                        if let Self::#variant_ident ( #pattern2_token_stream ) = source {
191                                            #body_token_stream
192                                        } else {
193                                            *self = ::core::clone::Clone::clone(source);
194                                        }
195                                    },
196                                });
197                        },
198                    }
199                }
200
201                if !contains_copy {
202                    clone_token_stream.extend(quote! {
203                        match self {
204                            #clone_variants_token_stream
205                        }
206                    });
207
208                    clone_from_token_stream.extend(quote! {
209                        match self {
210                            #clone_from_variants_token_stream
211                        }
212                    });
213                }
214            }
215
216            bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
217                &ast.generics.params,
218                &syn::parse2(if contains_copy {
219                    quote!(::core::marker::Copy)
220                } else {
221                    quote!(::core::clone::Clone)
222                })
223                .unwrap(),
224                &clone_types,
225                &[],
226            );
227        }
228
229        let clone_from_fn_token_stream = if clone_from_token_stream.is_empty() {
230            None
231        } else {
232            Some(quote! {
233                #[inline]
234                fn clone_from(&mut self, source: &Self) {
235                    #clone_from_token_stream
236                }
237            })
238        };
239
240        let ident = &ast.ident;
241
242        let mut generics = ast.generics.clone();
243        let where_clause = generics.make_where_clause();
244
245        for where_predicate in bound {
246            where_clause.predicates.push(where_predicate);
247        }
248
249        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
250
251        token_stream.extend(quote! {
252            impl #impl_generics ::core::clone::Clone for #ident #ty_generics #where_clause {
253                #[inline]
254                fn clone(&self) -> Self {
255                    #clone_token_stream
256                }
257
258                #clone_from_fn_token_stream
259            }
260        });
261
262        #[cfg(feature = "Copy")]
263        if traits.contains(&Trait::Copy) {
264            token_stream.extend(quote! {
265                impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause {
266                }
267            });
268        }
269
270        Ok(())
271    }
272}