educe/trait_handlers/clone/clone_enum.rs
1use std::{fmt::Write, str::FromStr};
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, Generics, Meta};
6
7use super::{
8 super::TraitHandler,
9 models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder},
10};
11use crate::Trait;
12
13pub struct CloneEnumHandler;
14
15impl TraitHandler for CloneEnumHandler {
16 fn trait_meta_handler(
17 ast: &DeriveInput,
18 tokens: &mut TokenStream,
19 traits: &[Trait],
20 meta: &Meta,
21 ) {
22 let type_attribute = TypeAttributeBuilder {
23 enable_flag: true, enable_bound: true
24 }
25 .from_clone_meta(meta);
26
27 let mut bound = Punctuated::new();
28
29 let mut clone_tokens = TokenStream::new();
30 let mut clone_from_tokens = TokenStream::new();
31
32 if let Data::Enum(data) = &ast.data {
33 let mut variant_idents = Vec::new();
34 let mut field_attributes_names: Vec<(bool, Vec<FieldAttribute>, Vec<String>)> =
35 Vec::new();
36
37 #[cfg(feature = "Copy")]
38 let mut has_custom_clone_method = false;
39
40 for variant in data.variants.iter() {
41 let _ = TypeAttributeBuilder {
42 enable_flag: false, enable_bound: false
43 }
44 .from_attributes(&variant.attrs, traits);
45
46 let mut field_attributes = Vec::new();
47 let mut field_names = Vec::new();
48 let mut is_tuple = true;
49
50 match &variant.fields {
51 Fields::Unit => (),
52 Fields::Named(fields) => {
53 // TODO Struct
54 is_tuple = false;
55
56 for field in fields.named.iter() {
57 let field_attribute = FieldAttributeBuilder {
58 enable_impl: true
59 }
60 .from_attributes(&field.attrs, traits);
61
62 let field_name = field.ident.as_ref().unwrap().to_string();
63
64 #[cfg(feature = "Copy")]
65 if field_attribute.clone_method.is_some() {
66 has_custom_clone_method = true;
67 }
68
69 field_attributes.push(field_attribute);
70 field_names.push(field_name);
71 }
72 },
73 Fields::Unnamed(fields) => {
74 // TODO Tuple
75 for (index, field) in fields.unnamed.iter().enumerate() {
76 let field_attribute = FieldAttributeBuilder {
77 enable_impl: true
78 }
79 .from_attributes(&field.attrs, traits);
80
81 let field_name = format!("_{}", index);
82
83 #[cfg(feature = "Copy")]
84 if field_attribute.clone_method.is_some() {
85 has_custom_clone_method = true;
86 }
87
88 field_attributes.push(field_attribute);
89 field_names.push(field_name);
90 }
91 },
92 }
93
94 variant_idents.push(variant.ident.to_string());
95 field_attributes_names.push((is_tuple, field_attributes, field_names));
96 }
97
98 let enum_name = ast.ident.to_string();
99
100 #[cfg(feature = "Copy")]
101 let contains_copy = !has_custom_clone_method && traits.contains(&Trait::Copy);
102
103 #[cfg(not(feature = "Copy"))]
104 let contains_copy = false;
105
106 if contains_copy {
107 bound = type_attribute
108 .bound
109 .into_punctuated_where_predicates_by_generic_parameters_with_copy(
110 &ast.generics.params,
111 );
112
113 clone_tokens.extend(quote!(*self));
114
115 let mut match_tokens = String::from("match self {");
116
117 for (index, variant_ident) in variant_idents.iter().enumerate() {
118 let field_attributes_names = &field_attributes_names[index];
119 let is_tuple = field_attributes_names.0;
120 let field_attributes = &field_attributes_names.1;
121 let field_names = &field_attributes_names.2;
122 let is_unit = field_names.is_empty();
123
124 if is_unit {
125 match_tokens
126 .write_fmt(format_args!(
127 "{enum_name}::{variant_ident} => {{ if let \
128 {enum_name}::{variant_ident} = _source {{ done = true; }} }}",
129 enum_name = enum_name,
130 variant_ident = variant_ident
131 ))
132 .unwrap();
133 } else {
134 let mut pattern_tokens = String::new();
135 let mut pattern_2_tokens = String::new();
136
137 if is_tuple {
138 for field_name in field_names {
139 pattern_tokens
140 .write_fmt(format_args!(
141 "{field_name},",
142 field_name = field_name
143 ))
144 .unwrap();
145 pattern_2_tokens
146 .write_fmt(format_args!(
147 "___{field_name},",
148 field_name = field_name
149 ))
150 .unwrap();
151 }
152 } else {
153 for field_name in field_names {
154 pattern_tokens
155 .write_fmt(format_args!(
156 "{field_name},",
157 field_name = field_name
158 ))
159 .unwrap();
160 pattern_2_tokens
161 .write_fmt(format_args!(
162 "{field_name}: ___{field_name},",
163 field_name = field_name
164 ))
165 .unwrap();
166 }
167 }
168
169 let mut block_tokens = String::new();
170
171 for (index, field_attribute) in field_attributes.iter().enumerate() {
172 let field_name = &field_names[index];
173
174 let clone_trait = &field_attribute.clone_trait;
175 let clone_method = &field_attribute.clone_method;
176
177 match clone_trait {
178 Some(clone_trait) => {
179 let clone_method = clone_method.as_ref().unwrap();
180
181 block_tokens
182 .write_fmt(format_args!(
183 "*{field_name} = \
184 {clone_trait}::{clone_method}(___{field_name});",
185 clone_trait = clone_trait,
186 clone_method = clone_method,
187 field_name = field_name
188 ))
189 .unwrap();
190 },
191 None => match clone_method {
192 Some(clone_method) => {
193 block_tokens
194 .write_fmt(format_args!(
195 "*{field_name} = {clone_method}(___{field_name});",
196 clone_method = clone_method,
197 field_name = field_name
198 ))
199 .unwrap();
200 },
201 None => {
202 block_tokens
203 .write_fmt(format_args!(
204 "core::clone::Clone::clone_from({field_name}, \
205 ___{field_name});",
206 field_name = field_name
207 ))
208 .unwrap();
209 },
210 },
211 }
212 }
213
214 if is_tuple {
215 match_tokens
216 .write_fmt(format_args!(
217 "{enum_name}::{variant_ident} ( {pattern_tokens} ) => {{ if \
218 let {enum_name}::{variant_ident} ( {pattern_2_tokens} ) = \
219 _source {{ {block_tokens} done = true; }} }}",
220 enum_name = enum_name,
221 variant_ident = variant_ident,
222 pattern_tokens = pattern_tokens,
223 pattern_2_tokens = pattern_2_tokens,
224 block_tokens = block_tokens
225 ))
226 .unwrap();
227 } else {
228 match_tokens
229 .write_fmt(format_args!(
230 "{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ if \
231 let {enum_name}::{variant_ident} {{ {pattern_2_tokens} }} = \
232 _source {{ {block_tokens} }} done = true; }}",
233 enum_name = enum_name,
234 variant_ident = variant_ident,
235 pattern_tokens = pattern_tokens,
236 pattern_2_tokens = pattern_2_tokens,
237 block_tokens = block_tokens
238 ))
239 .unwrap();
240 }
241 }
242 }
243
244 match_tokens.push('}');
245
246 clone_from_tokens.extend(TokenStream::from_str(&match_tokens).unwrap());
247 } else {
248 bound = type_attribute
249 .bound
250 .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params);
251
252 let mut clone_match_tokens = String::from("match self {");
253 let mut clone_from_match_tokens = String::from("match self {");
254
255 for (index, variant_ident) in variant_idents.iter().enumerate() {
256 let field_attributes_names = &field_attributes_names[index];
257 let is_tuple = field_attributes_names.0;
258 let field_attributes = &field_attributes_names.1;
259 let field_names = &field_attributes_names.2;
260 let is_unit = field_names.is_empty();
261
262 if is_unit {
263 clone_match_tokens
264 .write_fmt(format_args!(
265 "{enum_name}::{variant_ident} => {{ {enum_name}::{variant_ident} \
266 }}",
267 enum_name = enum_name,
268 variant_ident = variant_ident
269 ))
270 .unwrap();
271 clone_from_match_tokens
272 .write_fmt(format_args!(
273 "{enum_name}::{variant_ident} => {{ if let \
274 {enum_name}::{variant_ident} = _source {{ done = true; }} }}",
275 enum_name = enum_name,
276 variant_ident = variant_ident
277 ))
278 .unwrap();
279 } else {
280 let mut pattern_tokens = String::new();
281 let mut pattern_2_tokens = String::new();
282 let mut pattern_3_tokens = String::new();
283
284 if is_tuple {
285 let mut clone = format!(
286 "{enum_name}::{variant_ident}(",
287 enum_name = enum_name,
288 variant_ident = variant_ident
289 );
290 let mut clone_from = String::new();
291
292 for field_name in field_names {
293 pattern_tokens
294 .write_fmt(format_args!(
295 "{field_name},",
296 field_name = field_name
297 ))
298 .unwrap();
299 pattern_2_tokens
300 .write_fmt(format_args!(
301 "{field_name},",
302 field_name = field_name
303 ))
304 .unwrap();
305 pattern_3_tokens
306 .write_fmt(format_args!(
307 "___{field_name},",
308 field_name = field_name
309 ))
310 .unwrap();
311 }
312
313 for (index, field_attribute) in field_attributes.iter().enumerate() {
314 let field_name = &field_names[index];
315
316 let clone_trait = &field_attribute.clone_trait;
317 let clone_method = &field_attribute.clone_method;
318
319 match clone_trait {
320 Some(clone_trait) => {
321 let clone_method = clone_method.as_ref().unwrap();
322
323 clone
324 .write_fmt(format_args!(
325 "{clone_trait}::{clone_method}({field_name}),",
326 clone_trait = clone_trait,
327 clone_method = clone_method,
328 field_name = field_name
329 ))
330 .unwrap();
331 clone_from
332 .write_fmt(format_args!(
333 "*{field_name} = \
334 {clone_trait}::{clone_method}(___{field_name});",
335 clone_trait = clone_trait,
336 clone_method = clone_method,
337 field_name = field_name
338 ))
339 .unwrap();
340 },
341 None => {
342 match clone_method {
343 Some(clone_method) => {
344 clone
345 .write_fmt(format_args!(
346 "{clone_method}({field_name}),",
347 clone_method = clone_method,
348 field_name = field_name
349 ))
350 .unwrap();
351 clone_from
352 .write_fmt(format_args!(
353 "*{field_name} = \
354 {clone_method}(___{field_name});",
355 clone_method = clone_method,
356 field_name = field_name
357 ))
358 .unwrap();
359 },
360 None => {
361 clone
362 .write_fmt(format_args!(
363 "core::clone::Clone::clone({field_name}),",
364 field_name = field_name
365 ))
366 .unwrap();
367 clone_from.write_fmt(format_args!("core::clone::Clone::clone_from({field_name}, ___{field_name});", field_name = field_name)).unwrap();
368 },
369 }
370 },
371 }
372 }
373
374 clone.push(')');
375
376 clone_match_tokens
377 .write_fmt(format_args!(
378 "{enum_name}::{variant_ident} ( {pattern_tokens} ) => {{ \
379 {clone} }}",
380 enum_name = enum_name,
381 variant_ident = variant_ident,
382 pattern_tokens = pattern_tokens,
383 clone = clone
384 ))
385 .unwrap();
386 clone_from_match_tokens
387 .write_fmt(format_args!(
388 "{enum_name}::{variant_ident} ( {pattern_2_tokens} ) => {{ if \
389 let {enum_name}::{variant_ident} ( {pattern_3_tokens} ) = \
390 _source {{ {block_tokens} done = true; }} }}",
391 enum_name = enum_name,
392 variant_ident = variant_ident,
393 pattern_2_tokens = pattern_2_tokens,
394 pattern_3_tokens = pattern_3_tokens,
395 block_tokens = clone_from
396 ))
397 .unwrap();
398 } else {
399 let mut clone = format!(
400 "{enum_name}::{variant_ident} {{",
401 enum_name = enum_name,
402 variant_ident = variant_ident
403 );
404 let mut clone_from = String::new();
405
406 for field_name in field_names {
407 pattern_tokens
408 .write_fmt(format_args!(
409 "{field_name},",
410 field_name = field_name
411 ))
412 .unwrap();
413 pattern_2_tokens
414 .write_fmt(format_args!(
415 "{field_name},",
416 field_name = field_name
417 ))
418 .unwrap();
419 pattern_3_tokens
420 .write_fmt(format_args!(
421 "{field_name}: ___{field_name},",
422 field_name = field_name
423 ))
424 .unwrap();
425 }
426
427 for (index, field_attribute) in field_attributes.iter().enumerate() {
428 let field_name = &field_names[index];
429
430 let clone_trait = &field_attribute.clone_trait;
431 let clone_method = &field_attribute.clone_method;
432
433 match clone_trait {
434 Some(clone_trait) => {
435 let clone_method = clone_method.as_ref().unwrap();
436
437 clone
438 .write_fmt(format_args!(
439 "{field_name}: \
440 {clone_trait}::{clone_method}({field_name}),",
441 clone_trait = clone_trait,
442 clone_method = clone_method,
443 field_name = field_name
444 ))
445 .unwrap();
446 clone_from
447 .write_fmt(format_args!(
448 "*{field_name} = \
449 {clone_trait}::{clone_method}(___{field_name});",
450 clone_trait = clone_trait,
451 clone_method = clone_method,
452 field_name = field_name
453 ))
454 .unwrap();
455 },
456 None => {
457 match clone_method {
458 Some(clone_method) => {
459 clone
460 .write_fmt(format_args!(
461 "{field_name}: \
462 {clone_method}({field_name}),",
463 clone_method = clone_method,
464 field_name = field_name
465 ))
466 .unwrap();
467 clone_from
468 .write_fmt(format_args!(
469 "*{field_name} = \
470 {clone_method}(___{field_name});",
471 clone_method = clone_method,
472 field_name = field_name
473 ))
474 .unwrap();
475 },
476 None => {
477 clone
478 .write_fmt(format_args!(
479 "{field_name}: \
480 core::clone::Clone::clone({field_name}),",
481 field_name = field_name
482 ))
483 .unwrap();
484 clone_from.write_fmt(format_args!("core::clone::Clone::clone_from({field_name}, ___{field_name});", field_name = field_name)).unwrap();
485 },
486 }
487 },
488 }
489 }
490
491 clone.push('}');
492
493 clone_match_tokens
494 .write_fmt(format_args!(
495 "{enum_name}::{variant_ident} {{ {pattern_tokens} }} => {{ \
496 {clone} }}",
497 enum_name = enum_name,
498 variant_ident = variant_ident,
499 pattern_tokens = pattern_tokens,
500 clone = clone
501 ))
502 .unwrap();
503 clone_from_match_tokens
504 .write_fmt(format_args!(
505 "{enum_name}::{variant_ident} {{ {pattern_2_tokens} }} => {{ \
506 if let {enum_name}::{variant_ident} {{ {pattern_3_tokens} }} \
507 = _source {{ {block_tokens} }} done = true; }}",
508 enum_name = enum_name,
509 variant_ident = variant_ident,
510 pattern_2_tokens = pattern_2_tokens,
511 pattern_3_tokens = pattern_3_tokens,
512 block_tokens = clone_from
513 ))
514 .unwrap();
515 }
516 }
517 }
518
519 clone_match_tokens.push('}');
520 clone_from_match_tokens.push('}');
521
522 clone_tokens.extend(TokenStream::from_str(&clone_match_tokens).unwrap());
523 clone_from_tokens.extend(TokenStream::from_str(&clone_from_match_tokens).unwrap());
524 }
525 }
526
527 let ident = &ast.ident;
528
529 let mut generics_cloned: Generics = ast.generics.clone();
530
531 let where_clause = generics_cloned.make_where_clause();
532
533 for where_predicate in bound {
534 where_clause.predicates.push(where_predicate);
535 }
536
537 let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl();
538
539 let compare_impl = quote! {
540 impl #impl_generics core::clone::Clone for #ident #ty_generics #where_clause {
541 #[inline]
542 fn clone(&self) -> Self {
543 #clone_tokens
544 }
545
546 #[allow(clippy::incorrect_clone_impl_on_copy_type)]
547 #[inline]
548 fn clone_from(&mut self, _source: &Self) {
549 let mut done = false;
550
551 #clone_from_tokens
552
553 if !done {
554 *self = core::clone::Clone::clone(_source);
555 }
556 }
557 }
558 };
559
560 tokens.extend(compare_impl);
561 }
562}