educe/trait_handlers/debug/
debug_struct.rs1use quote::{format_ident, quote};
2use syn::{Data, DeriveInput, Fields, Meta, Type};
3
4use super::{
5 models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName},
6 TraitHandler,
7};
8use crate::{common::ident_index::IdentOrIndex, Trait};
9
10pub struct DebugStructHandler;
11
12impl TraitHandler for DebugStructHandler {
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 is_tuple = {
20 if let Data::Struct(data) = &ast.data {
21 matches!(data.fields, Fields::Unnamed(_))
22 } else {
23 true
24 }
25 };
26
27 let type_attribute = TypeAttributeBuilder {
28 enable_flag: true,
29 enable_unsafe: false,
30 enable_name: true,
31 enable_named_field: true,
32 enable_bound: true,
33 name: TypeName::Default,
34 named_field: !is_tuple,
35 }
36 .build_from_debug_meta(meta)?;
37
38 let name = type_attribute.name.to_ident_by_ident(&ast.ident);
39
40 let mut debug_types: Vec<&Type> = Vec::new();
41
42 let mut builder_token_stream = proc_macro2::TokenStream::new();
43 let mut has_fields = false;
44
45 if type_attribute.named_field {
46 builder_token_stream.extend(if let Some(name) = name {
47 quote!(let mut builder = f.debug_struct(stringify!(#name));)
48 } else {
49 super::common::create_debug_map_builder()
50 });
51
52 if let Data::Struct(data) = &ast.data {
53 for (index, field) in data.fields.iter().enumerate() {
54 let field_attribute = FieldAttributeBuilder {
55 enable_name: true,
56 enable_ignore: true,
57 enable_method: true,
58 name: FieldName::Default,
59 }
60 .build_from_attributes(&field.attrs, traits)?;
61
62 if field_attribute.ignore {
63 continue;
64 }
65
66 let (key, field_name) = match field_attribute.name {
67 FieldName::Custom(name) => {
68 (name, IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index))
69 },
70 FieldName::Default => {
71 if let Some(ident) = field.ident.as_ref() {
72 (ident.clone(), IdentOrIndex::from(ident))
73 } else {
74 (format_ident!("_{}", index), IdentOrIndex::from(index))
75 }
76 },
77 };
78
79 let ty = &field.ty;
80
81 if let Some(method) = field_attribute.method {
82 builder_token_stream.extend(super::common::create_format_arg(
83 ast,
84 ty,
85 &method,
86 quote!(&self.#field_name),
87 ));
88
89 builder_token_stream.extend(if name.is_some() {
90 quote! (builder.field(stringify!(#key), &arg);)
91 } else {
92 quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);)
93 });
94 } else {
95 debug_types.push(ty);
96
97 builder_token_stream.extend(if name.is_some() {
98 quote! (builder.field(stringify!(#key), &self.#field_name);)
99 } else {
100 quote! (builder.entry(&Educe__RawString(stringify!(#key)), &self.#field_name);)
101 });
102 }
103
104 has_fields = true;
105 }
106 }
107 } else {
108 builder_token_stream
109 .extend(quote!(let mut builder = f.debug_tuple(stringify!(#name));));
110
111 if let Data::Struct(data) = &ast.data {
112 for (index, field) in data.fields.iter().enumerate() {
113 let field_attribute = FieldAttributeBuilder {
114 enable_name: false,
115 enable_ignore: true,
116 enable_method: true,
117 name: FieldName::Default,
118 }
119 .build_from_attributes(&field.attrs, traits)?;
120
121 if field_attribute.ignore {
122 continue;
123 }
124
125 let field_name =
126 IdentOrIndex::from_ident_with_index(field.ident.as_ref(), index);
127
128 let ty = &field.ty;
129
130 if let Some(method) = field_attribute.method {
131 builder_token_stream.extend(super::common::create_format_arg(
132 ast,
133 ty,
134 &method,
135 quote!(&self.#field_name),
136 ));
137
138 builder_token_stream.extend(quote! (builder.field(&arg);));
139 } else {
140 debug_types.push(ty);
141
142 builder_token_stream.extend(quote! (builder.field(&self.#field_name);));
143 }
144
145 has_fields = true;
146 }
147 }
148 }
149
150 let ident = &ast.ident;
151
152 if !has_fields && name.is_none() {
153 return Err(super::panic::unit_struct_need_name(ident));
154 }
155
156 let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
157 &ast.generics.params,
158 &syn::parse2(quote!(::core::fmt::Debug)).unwrap(),
159 &debug_types,
160 &[],
161 );
162
163 let mut generics = ast.generics.clone();
164 let where_clause = generics.make_where_clause();
165
166 for where_predicate in bound {
167 where_clause.predicates.push(where_predicate);
168 }
169
170 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
171
172 token_stream.extend(quote! {
173 impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
174 #[inline]
175 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
176 #builder_token_stream
177
178 builder.finish()
179 }
180 }
181 });
182
183 Ok(())
184 }
185}