educe/trait_handlers/clone/
clone_enum.rs1use 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 } 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}