educe/trait_handlers/debug/debug_enum.rs
1use std::{fmt::Write, str::FromStr};
2
3use proc_macro2::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{Data, DeriveInput, Fields, Generics, Meta};
6
7use super::{
8 super::TraitHandler,
9 models::{FieldAttributeBuilder, FieldAttributeName, TypeAttributeBuilder, TypeAttributeName},
10};
11use crate::{panic, Trait};
12
13pub struct DebugEnumHandler;
14
15impl TraitHandler for DebugEnumHandler {
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,
24 name: TypeAttributeName::Disable,
25 enable_name: true,
26 named_field: false,
27 enable_named_field: false,
28 enable_bound: true,
29 }
30 .from_debug_meta(meta);
31
32 let enum_name = ast.ident.to_string();
33
34 let name = type_attribute.name.into_string_by_ident(&ast.ident);
35
36 let bound = type_attribute
37 .bound
38 .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params);
39
40 let mut builder_tokens = TokenStream::new();
41 let mut has_variants = false;
42
43 let mut match_tokens = String::from("match self {");
44
45 if let Data::Enum(data) = &ast.data {
46 for variant in data.variants.iter() {
47 let type_attribute = TypeAttributeBuilder {
48 enable_flag: false,
49 name: TypeAttributeName::Default,
50 enable_name: true,
51 named_field: matches!(&variant.fields, Fields::Named(_)),
52 enable_named_field: true,
53 enable_bound: false,
54 }
55 .from_attributes(&variant.attrs, traits);
56
57 let variant_name = type_attribute.name.into_string_by_ident(&variant.ident);
58
59 let named_field = type_attribute.named_field;
60
61 let variant_ident = variant.ident.to_string();
62
63 let name = combine_names(&name, variant_name);
64
65 match &variant.fields {
66 Fields::Unit => {
67 // TODO Unit
68 if name.is_empty() {
69 panic::unit_variant_need_name();
70 }
71
72 match_tokens
73 .write_fmt(format_args!(
74 "{enum_name}::{variant_ident} => {{ formatter.write_str({name:?}) \
75 }}",
76 enum_name = enum_name,
77 variant_ident = variant_ident,
78 name = name
79 ))
80 .unwrap();
81
82 has_variants = true;
83 },
84 Fields::Named(fields) => {
85 // TODO Struct
86 let mut has_fields = false;
87
88 let mut pattern_tokens = String::new();
89 let mut block_tokens = String::new();
90
91 if named_field {
92 if name.is_empty() {
93 block_tokens.push_str(
94 "
95 struct RawString(&'static str);
96
97 impl core::fmt::Debug for RawString {{
98 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> \
99 core::fmt::Result {{
100 f.write_str(self.0)
101 }}
102 }}
103 ",
104 );
105
106 block_tokens.push_str("let mut builder = formatter.debug_map();");
107 } else {
108 block_tokens
109 .write_fmt(format_args!(
110 "let mut builder = formatter.debug_struct({name:?});",
111 name = name
112 ))
113 .unwrap();
114 }
115
116 for field in fields.named.iter() {
117 let field_attribute = FieldAttributeBuilder {
118 name: FieldAttributeName::Default,
119 enable_name: true,
120 enable_ignore: true,
121 enable_impl: true,
122 }
123 .from_attributes(&field.attrs, traits);
124
125 let field_name = field.ident.as_ref().unwrap().to_string();
126
127 if field_attribute.ignore {
128 pattern_tokens
129 .write_fmt(format_args!(
130 "{field_name}: _,",
131 field_name = field_name
132 ))
133 .unwrap();
134 continue;
135 }
136
137 let rename = field_attribute.name.into_option_string();
138
139 let format_trait = field_attribute.format_trait;
140 let format_method = field_attribute.format_method;
141
142 let key = rename.unwrap_or_else(|| field_name.clone());
143
144 pattern_tokens
145 .write_fmt(format_args!(
146 "{field_name},",
147 field_name = field_name
148 ))
149 .unwrap();
150
151 match format_trait {
152 Some(format_trait) => {
153 let format_method = format_method.unwrap();
154
155 block_tokens
156 .write_fmt(format_args!(
157 "
158 let arg = {{
159 struct MyDebug<'a, T: {format_trait}>(&'a T);
160
161 impl<'a, T: {format_trait}> core::fmt::Debug for \
162 MyDebug<'a, T> {{
163 fn fmt(&self, formatter: &mut \
164 core::fmt::Formatter) -> core::fmt::Result {{
165 {format_trait}::{format_method}(self.0, \
166 formatter)
167 }}
168 }}
169
170 MyDebug({field_name})
171 }};
172 ",
173 format_trait = format_trait,
174 format_method = format_method,
175 field_name = field_name
176 ))
177 .unwrap();
178
179 let statement = if name.is_empty() {
180 format!(
181 "builder.entry(&RawString({key:?}), &arg);",
182 key = key
183 )
184 } else {
185 format!("builder.field({key:?}, &arg);", key = key)
186 };
187
188 block_tokens.push_str(&statement);
189 },
190 None => match format_method {
191 Some(format_method) => {
192 let ty =
193 field.ty.clone().into_token_stream().to_string();
194
195 block_tokens
196 .write_fmt(format_args!(
197 "
198 let arg = {{
199 struct MyDebug<'a>(&'a {ty});
200
201 impl<'a> core::fmt::Debug for MyDebug<'a> \
202 {{
203 fn fmt(&self, formatter: &mut \
204 core::fmt::Formatter) -> core::fmt::Result {{
205 {format_method}(self.0, formatter)
206 }}
207 }}
208
209 MyDebug({field_name})
210 }};
211 ",
212 ty = ty,
213 format_method = format_method,
214 field_name = field_name
215 ))
216 .unwrap();
217
218 let statement = if name.is_empty() {
219 format!(
220 "builder.entry(&RawString({key:?}), &arg);",
221 key = key
222 )
223 } else {
224 format!("builder.field({key:?}, &arg);", key = key)
225 };
226
227 block_tokens.push_str(&statement);
228 },
229 None => {
230 let statement = if name.is_empty() {
231 format!(
232 "builder.entry(&RawString({key:?}), \
233 {field_name});",
234 key = key,
235 field_name = field_name
236 )
237 } else {
238 format!(
239 "builder.field({key:?}, {field_name});",
240 key = key,
241 field_name = field_name
242 )
243 };
244
245 block_tokens.push_str(&statement);
246 },
247 },
248 }
249
250 has_fields = true;
251 }
252 } else {
253 block_tokens
254 .write_fmt(format_args!(
255 "let mut builder = formatter.debug_tuple({name:?});",
256 name = name
257 ))
258 .unwrap();
259
260 for field in fields.named.iter() {
261 let field_attribute = FieldAttributeBuilder {
262 name: FieldAttributeName::Default,
263 enable_name: false,
264 enable_ignore: true,
265 enable_impl: true,
266 }
267 .from_attributes(&field.attrs, traits);
268
269 let field_name = field.ident.as_ref().unwrap().to_string();
270
271 if field_attribute.ignore {
272 pattern_tokens
273 .write_fmt(format_args!(
274 "{field_name}: _,",
275 field_name = field_name
276 ))
277 .unwrap();
278 continue;
279 }
280
281 let format_trait = field_attribute.format_trait;
282 let format_method = field_attribute.format_method;
283
284 pattern_tokens
285 .write_fmt(format_args!(
286 "{field_name},",
287 field_name = field_name
288 ))
289 .unwrap();
290
291 match format_trait {
292 Some(format_trait) => {
293 let format_method = format_method.unwrap();
294
295 block_tokens
296 .write_fmt(format_args!(
297 "
298 let arg = {{
299 struct MyDebug<'a, T: {format_trait}>(&'a T);
300
301 impl<'a, T: {format_trait}> core::fmt::Debug for \
302 MyDebug<'a, T> {{
303 fn fmt(&self, formatter: &mut \
304 core::fmt::Formatter) -> core::fmt::Result {{
305 {format_trait}::{format_method}(self.0, \
306 formatter)
307 }}
308 }}
309
310 MyDebug({field_name})
311 }};
312 ",
313 format_trait = format_trait,
314 format_method = format_method,
315 field_name = field_name
316 ))
317 .unwrap();
318
319 block_tokens.push_str("builder.field(&arg);");
320 },
321 None => match format_method {
322 Some(format_method) => {
323 let ty =
324 field.ty.clone().into_token_stream().to_string();
325
326 block_tokens
327 .write_fmt(format_args!(
328 "
329 let arg = {{
330 struct MyDebug<'a>(&'a {ty});
331
332 impl<'a> core::fmt::Debug for MyDebug<'a> \
333 {{
334 fn fmt(&self, formatter: &mut \
335 core::fmt::Formatter) -> core::fmt::Result {{
336 {format_method}(self.0, formatter)
337 }}
338 }}
339
340 MyDebug({field_name})
341 }};
342 ",
343 ty = ty,
344 format_method = format_method,
345 field_name = field_name
346 ))
347 .unwrap();
348
349 block_tokens.push_str("builder.field(&arg);");
350 },
351 None => {
352 let statement = format!(
353 "builder.field({field_name});",
354 field_name = field_name
355 );
356
357 block_tokens.push_str(&statement);
358 },
359 },
360 }
361
362 has_fields = true;
363 }
364 }
365
366 if name.is_empty() && !has_fields {
367 panic::unit_struct_need_name();
368 }
369
370 block_tokens.push_str("return builder.finish();");
371
372 match_tokens
373 .write_fmt(format_args!(
374 "{enum_name}::{variant_ident}{{ {pattern_tokens} }} => {{ \
375 {block_tokens} }}",
376 enum_name = enum_name,
377 variant_ident = variant_ident,
378 pattern_tokens = pattern_tokens,
379 block_tokens = block_tokens
380 ))
381 .unwrap();
382
383 has_variants = true;
384 },
385 Fields::Unnamed(fields) => {
386 // TODO Tuple
387 let mut has_fields = false;
388
389 let mut pattern_tokens = String::new();
390 let mut block_tokens = String::new();
391
392 if named_field {
393 if name.is_empty() {
394 block_tokens.push_str(
395 "
396 struct RawString(&'static str);
397
398 impl core::fmt::Debug for RawString {{
399 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> \
400 core::fmt::Result {{
401 f.write_str(self.0)
402 }}
403 }}
404 ",
405 );
406
407 block_tokens.push_str("let mut builder = formatter.debug_map();");
408 } else {
409 block_tokens
410 .write_fmt(format_args!(
411 "let mut builder = formatter.debug_struct({name:?});",
412 name = name
413 ))
414 .unwrap();
415 }
416
417 for (index, field) in fields.unnamed.iter().enumerate() {
418 let field_attribute = FieldAttributeBuilder {
419 name: FieldAttributeName::Default,
420 enable_name: true,
421 enable_ignore: true,
422 enable_impl: true,
423 }
424 .from_attributes(&field.attrs, traits);
425
426 if field_attribute.ignore {
427 pattern_tokens.push_str("_,");
428 continue;
429 }
430
431 let rename = field_attribute.name.into_option_string();
432
433 let format_trait = field_attribute.format_trait;
434 let format_method = field_attribute.format_method;
435
436 let (key, field_name) = match rename {
437 Some(rename) => (rename, format!("{}", index)),
438 None => (format!("_{}", index), format!("{}", index)),
439 };
440
441 pattern_tokens
442 .write_fmt(format_args!(
443 "_{field_name},",
444 field_name = field_name
445 ))
446 .unwrap();
447
448 match format_trait {
449 Some(format_trait) => {
450 let format_method = format_method.unwrap();
451
452 block_tokens
453 .write_fmt(format_args!(
454 "
455 let arg = {{
456 struct MyDebug<'a, T: {format_trait}>(&'a T);
457
458 impl<'a, T: {format_trait}> core::fmt::Debug for \
459 MyDebug<'a, T> {{
460 fn fmt(&self, formatter: &mut \
461 core::fmt::Formatter) -> core::fmt::Result {{
462 {format_trait}::{format_method}(self.0, \
463 formatter)
464 }}
465 }}
466
467 MyDebug(_{field_name})
468 }};
469 ",
470 format_trait = format_trait,
471 format_method = format_method,
472 field_name = field_name
473 ))
474 .unwrap();
475
476 let statement = if name.is_empty() {
477 format!(
478 "builder.entry(&RawString({key:?}), &arg);",
479 key = key
480 )
481 } else {
482 format!("builder.field({key:?}, &arg);", key = key)
483 };
484
485 block_tokens.push_str(&statement);
486 },
487 None => match format_method {
488 Some(format_method) => {
489 let ty =
490 field.ty.clone().into_token_stream().to_string();
491
492 block_tokens
493 .write_fmt(format_args!(
494 "
495 let arg = {{
496 struct MyDebug<'a>(&'a {ty});
497
498 impl<'a> core::fmt::Debug for MyDebug<'a> \
499 {{
500 fn fmt(&self, formatter: &mut \
501 core::fmt::Formatter) -> core::fmt::Result {{
502 {format_method}(self.0, formatter)
503 }}
504 }}
505
506 MyDebug(_{field_name})
507 }};
508 ",
509 ty = ty,
510 format_method = format_method,
511 field_name = field_name
512 ))
513 .unwrap();
514
515 let statement = if name.is_empty() {
516 format!(
517 "builder.entry(&RawString({key:?}), &arg);",
518 key = key
519 )
520 } else {
521 format!("builder.field({key:?}, &arg);", key = key)
522 };
523
524 block_tokens.push_str(&statement);
525 },
526 None => {
527 let statement = if name.is_empty() {
528 format!(
529 "builder.entry(&RawString({key:?}), \
530 {field_name});",
531 key = key,
532 field_name = field_name
533 )
534 } else {
535 format!(
536 "builder.field({key:?}, _{field_name});",
537 key = key,
538 field_name = field_name
539 )
540 };
541
542 block_tokens.push_str(&statement);
543 },
544 },
545 }
546
547 has_fields = true;
548 }
549 } else {
550 block_tokens
551 .write_fmt(format_args!(
552 "let mut builder = formatter.debug_tuple({name:?});",
553 name = name
554 ))
555 .unwrap();
556
557 for (index, field) in fields.unnamed.iter().enumerate() {
558 let field_attribute = FieldAttributeBuilder {
559 name: FieldAttributeName::Default,
560 enable_name: false,
561 enable_ignore: true,
562 enable_impl: true,
563 }
564 .from_attributes(&field.attrs, traits);
565
566 if field_attribute.ignore {
567 pattern_tokens.push_str("_,");
568 continue;
569 }
570
571 let format_trait = field_attribute.format_trait;
572 let format_method = field_attribute.format_method;
573
574 let field_name = format!("{}", index);
575
576 pattern_tokens
577 .write_fmt(format_args!(
578 "_{field_name},",
579 field_name = field_name
580 ))
581 .unwrap();
582
583 match format_trait {
584 Some(format_trait) => {
585 let format_method = format_method.unwrap();
586
587 block_tokens
588 .write_fmt(format_args!(
589 "
590 let arg = {{
591 struct MyDebug<'a, T: {format_trait}>(&'a T);
592
593 impl<'a, T: {format_trait}> core::fmt::Debug for \
594 MyDebug<'a, T> {{
595 fn fmt(&self, formatter: &mut \
596 core::fmt::Formatter) -> core::fmt::Result {{
597 {format_trait}::{format_method}(self.0, \
598 formatter)
599 }}
600 }}
601
602 MyDebug(_{field_name})
603 }};
604 ",
605 format_trait = format_trait,
606 format_method = format_method,
607 field_name = field_name
608 ))
609 .unwrap();
610
611 block_tokens.push_str("builder.field(&arg);");
612 },
613 None => match format_method {
614 Some(format_method) => {
615 let ty =
616 field.ty.clone().into_token_stream().to_string();
617
618 block_tokens
619 .write_fmt(format_args!(
620 "
621 let arg = {{
622 struct MyDebug<'a>(&'a {ty});
623
624 impl<'a> core::fmt::Debug for MyDebug<'a> \
625 {{
626 fn fmt(&self, formatter: &mut \
627 core::fmt::Formatter) -> core::fmt::Result {{
628 {format_method}(self.0, formatter)
629 }}
630 }}
631
632 MyDebug(_{field_name})
633 }};
634 ",
635 ty = ty,
636 format_method = format_method,
637 field_name = field_name
638 ))
639 .unwrap();
640
641 block_tokens.push_str("builder.field(&arg);");
642 },
643 None => {
644 let statement = format!(
645 "builder.field(_{field_name});",
646 field_name = field_name
647 );
648
649 block_tokens.push_str(&statement);
650 },
651 },
652 }
653
654 has_fields = true;
655 }
656 }
657
658 if name.is_empty() && !has_fields {
659 panic::unit_struct_need_name();
660 }
661
662 block_tokens.push_str("return builder.finish();");
663
664 match_tokens
665 .write_fmt(format_args!(
666 "{enum_name}::{variant_ident}( {pattern_tokens} ) => {{ \
667 {block_tokens} }}",
668 enum_name = enum_name,
669 variant_ident = variant_ident,
670 pattern_tokens = pattern_tokens,
671 block_tokens = block_tokens
672 ))
673 .unwrap();
674
675 has_variants = true;
676 },
677 }
678 }
679 }
680
681 match_tokens.push('}');
682
683 builder_tokens.extend(TokenStream::from_str(&match_tokens).unwrap());
684
685 if name.is_empty() && !has_variants {
686 panic::unit_enum_need_name();
687 }
688
689 let ident = &ast.ident;
690
691 let mut generics_cloned: Generics = ast.generics.clone();
692
693 let where_clause = generics_cloned.make_where_clause();
694
695 for where_predicate in bound {
696 where_clause.predicates.push(where_predicate);
697 }
698
699 let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl();
700
701 let debug_impl = quote! {
702 impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause {
703 #[inline]
704 #[allow(clippy::unneeded_field_pattern)]
705 fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
706 #builder_tokens
707 }
708 }
709 };
710
711 tokens.extend(debug_impl);
712 }
713}
714
715fn combine_names(name: &str, variant_name: String) -> String {
716 if name.is_empty() {
717 if variant_name.is_empty() {
718 String::new()
719 } else {
720 variant_name
721 }
722 } else {
723 let mut name = name.to_string();
724
725 if !variant_name.is_empty() {
726 if !variant_name.starts_with("::") {
727 name.push_str("::");
728 }
729
730 name.push_str(&variant_name);
731 }
732
733 name
734 }
735}