educe/trait_handlers/debug/
debug_enum.rs1use quote::{format_ident, quote, ToTokens};
2use syn::{Data, DeriveInput, Fields, Meta, Type};
3
4use super::models::{FieldAttributeBuilder, FieldName, TypeAttributeBuilder, TypeName};
5use crate::{common::path::path_to_string, supported_traits::Trait, trait_handlers::TraitHandler};
6
7pub(crate) struct DebugEnumHandler;
8
9impl TraitHandler for DebugEnumHandler {
10 fn trait_meta_handler(
11 ast: &DeriveInput,
12 token_stream: &mut proc_macro2::TokenStream,
13 traits: &[Trait],
14 meta: &Meta,
15 ) -> syn::Result<()> {
16 let type_attribute = TypeAttributeBuilder {
17 enable_flag: true,
18 enable_unsafe: false,
19 enable_name: true,
20 enable_named_field: false,
21 enable_bound: true,
22 name: TypeName::Disable,
23 named_field: false,
24 }
25 .build_from_debug_meta(meta)?;
26
27 let name = type_attribute.name.to_ident_by_ident(&ast.ident);
28
29 let mut debug_types: Vec<&Type> = Vec::new();
30
31 let mut builder_token_stream = proc_macro2::TokenStream::new();
32
33 let mut arms_token_stream = proc_macro2::TokenStream::new();
34
35 if let Data::Enum(data) = &ast.data {
36 for variant in data.variants.iter() {
37 let type_attribute = TypeAttributeBuilder {
38 enable_flag: false,
39 enable_unsafe: false,
40 enable_name: true,
41 enable_named_field: true,
42 enable_bound: false,
43 name: TypeName::Default,
44 named_field: matches!(&variant.fields, Fields::Named(_)),
45 }
46 .build_from_attributes(&variant.attrs, traits)?;
47
48 let variant_ident = &variant.ident;
49
50 let variant_name = type_attribute.name.to_ident_by_ident(variant_ident);
51
52 let named_field = type_attribute.named_field;
53
54 let name_string = if let Some(name) = name {
55 if let Some(variant_name) = variant_name {
56 Some(path_to_string(&syn::parse2(quote!(#name::#variant_name)).unwrap()))
57 } else {
58 Some(name.into_token_stream().to_string())
59 }
60 } else {
61 variant_name.map(|variant_name| variant_name.into_token_stream().to_string())
62 };
63
64 match &variant.fields {
65 Fields::Unit => {
66 if name_string.is_none() {
67 return Err(super::panic::unit_variant_need_name(variant));
68 }
69
70 arms_token_stream
71 .extend(quote!( Self::#variant_ident => f.write_str(#name_string), ));
72 },
73 Fields::Named(fields) => {
74 let mut has_fields = false;
75
76 let mut pattern_token_stream = proc_macro2::TokenStream::new();
77 let mut block_token_stream = proc_macro2::TokenStream::new();
78
79 if named_field {
80 block_token_stream
81 .extend(create_named_field_builder(name_string.as_deref()));
82
83 for field in fields.named.iter() {
84 let field_attribute = FieldAttributeBuilder {
85 enable_name: true,
86 enable_ignore: true,
87 enable_method: true,
88 name: FieldName::Default,
89 }
90 .build_from_attributes(&field.attrs, traits)?;
91
92 let field_name_real = field.ident.as_ref().unwrap();
93 let field_name_var = format_ident!("_{}", field_name_real);
94
95 if field_attribute.ignore {
96 pattern_token_stream.extend(quote!(#field_name_real: _,));
97
98 continue;
99 }
100
101 let key = match field_attribute.name {
102 FieldName::Custom(name) => name,
103 FieldName::Default => field_name_real.clone(),
104 };
105
106 pattern_token_stream
107 .extend(quote!(#field_name_real: #field_name_var,));
108
109 let ty = &field.ty;
110
111 if let Some(method) = field_attribute.method {
112 block_token_stream.extend(super::common::create_format_arg(
113 ast,
114 ty,
115 &method,
116 quote!(#field_name_var),
117 ));
118
119 block_token_stream.extend(if name_string.is_some() {
120 quote! (builder.field(stringify!(#key), &arg);)
121 } else {
122 quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);)
123 });
124 } else {
125 debug_types.push(ty);
126
127 block_token_stream.extend(if name_string.is_some() {
128 quote! (builder.field(stringify!(#key), #field_name_var);)
129 } else {
130 quote! (builder.entry(&Educe__RawString(stringify!(#key)), #field_name_var);)
131 });
132 }
133
134 has_fields = true;
135 }
136 } else {
137 block_token_stream
138 .extend(quote!(let mut builder = f.debug_tuple(#name_string);));
139
140 for field in fields.named.iter() {
141 let field_attribute = FieldAttributeBuilder {
142 enable_name: false,
143 enable_ignore: true,
144 enable_method: true,
145 name: FieldName::Default,
146 }
147 .build_from_attributes(&field.attrs, traits)?;
148
149 let field_name_real = field.ident.as_ref().unwrap();
150 let field_name_var = format_ident!("_{}", field_name_real);
151
152 if field_attribute.ignore {
153 pattern_token_stream.extend(quote!(#field_name_real: _,));
154
155 continue;
156 }
157
158 pattern_token_stream
159 .extend(quote!(#field_name_real: #field_name_var,));
160
161 let ty = &field.ty;
162
163 if let Some(method) = field_attribute.method {
164 block_token_stream.extend(super::common::create_format_arg(
165 ast,
166 ty,
167 &method,
168 quote!(#field_name_var),
169 ));
170
171 block_token_stream.extend(quote! (builder.field(&arg);));
172 } else {
173 debug_types.push(ty);
174
175 block_token_stream
176 .extend(quote! (builder.field(#field_name_var);));
177 }
178
179 has_fields = true;
180 }
181 }
182
183 if !has_fields && name_string.is_none() {
184 return Err(super::panic::unit_struct_need_name(variant_ident));
185 }
186
187 arms_token_stream.extend(quote! {
188 Self::#variant_ident { #pattern_token_stream } => {
189 #block_token_stream
190
191 builder.finish()
192 },
193 });
194 },
195 Fields::Unnamed(fields) => {
196 let mut has_fields = false;
197
198 let mut pattern_token_stream = proc_macro2::TokenStream::new();
199 let mut block_token_stream = proc_macro2::TokenStream::new();
200
201 if named_field {
202 block_token_stream
203 .extend(create_named_field_builder(name_string.as_deref()));
204
205 for (index, field) in fields.unnamed.iter().enumerate() {
206 let field_attribute = FieldAttributeBuilder {
207 enable_name: true,
208 enable_ignore: true,
209 enable_method: true,
210 name: FieldName::Default,
211 }
212 .build_from_attributes(&field.attrs, traits)?;
213
214 if field_attribute.ignore {
215 pattern_token_stream.extend(quote!(_,));
216
217 continue;
218 }
219
220 let field_name_var = format_ident!("_{}", index);
221
222 let key = match field_attribute.name {
223 FieldName::Custom(name) => name,
224 FieldName::Default => field_name_var.clone(),
225 };
226
227 pattern_token_stream.extend(quote!(#field_name_var,));
228
229 let ty = &field.ty;
230
231 if let Some(method) = field_attribute.method {
232 block_token_stream.extend(super::common::create_format_arg(
233 ast,
234 ty,
235 &method,
236 quote!(#field_name_var),
237 ));
238
239 block_token_stream.extend(if name_string.is_some() {
240 quote! (builder.field(stringify!(#key), &arg);)
241 } else {
242 quote! (builder.entry(&Educe__RawString(stringify!(#key)), &arg);)
243 });
244 } else {
245 debug_types.push(ty);
246
247 block_token_stream.extend(if name_string.is_some() {
248 quote! (builder.field(stringify!(#key), #field_name_var);)
249 } else {
250 quote! (builder.entry(&Educe__RawString(stringify!(#key)), #field_name_var);)
251 });
252 }
253
254 has_fields = true;
255 }
256 } else {
257 block_token_stream
258 .extend(quote!(let mut builder = f.debug_tuple(#name_string);));
259
260 for (index, field) in fields.unnamed.iter().enumerate() {
261 let field_attribute = FieldAttributeBuilder {
262 enable_name: false,
263 enable_ignore: true,
264 enable_method: true,
265 name: FieldName::Default,
266 }
267 .build_from_attributes(&field.attrs, traits)?;
268
269 if field_attribute.ignore {
270 pattern_token_stream.extend(quote!(_,));
271
272 continue;
273 }
274
275 let field_name_var = format_ident!("_{}", index);
276
277 pattern_token_stream.extend(quote!(#field_name_var,));
278
279 let ty = &field.ty;
280
281 if let Some(method) = field_attribute.method {
282 block_token_stream.extend(super::common::create_format_arg(
283 ast,
284 ty,
285 &method,
286 quote!(#field_name_var),
287 ));
288
289 block_token_stream.extend(quote! (builder.field(&arg);));
290 } else {
291 debug_types.push(ty);
292
293 block_token_stream
294 .extend(quote! (builder.field(#field_name_var);));
295 }
296
297 has_fields = true;
298 }
299 }
300
301 if !has_fields && name_string.is_none() {
302 return Err(super::panic::unit_struct_need_name(variant_ident));
303 }
304
305 arms_token_stream.extend(quote! {
306 Self::#variant_ident ( #pattern_token_stream ) => {
307 #block_token_stream
308
309 builder.finish()
310 },
311 });
312 },
313 }
314 }
315 }
316
317 let ident = &ast.ident;
318
319 if arms_token_stream.is_empty() {
320 if let Some(ident) = name {
321 builder_token_stream.extend(quote! {
322 f.write_str(stringify!(#ident))
323 });
324 } else {
325 return Err(super::panic::unit_enum_need_name(ident));
326 }
327 } else {
328 builder_token_stream.extend(quote! {
329 match self {
330 #arms_token_stream
331 }
332 });
333 }
334
335 let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
336 &ast.generics.params,
337 &syn::parse2(quote!(::core::fmt::Debug)).unwrap(),
338 &debug_types,
339 &[],
340 );
341
342 let mut generics = ast.generics.clone();
343 let where_clause = generics.make_where_clause();
344
345 for where_predicate in bound {
346 where_clause.predicates.push(where_predicate);
347 }
348
349 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
350
351 token_stream.extend(quote! {
352 impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
353 #[inline]
354 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
355 #builder_token_stream
356 }
357 }
358 });
359
360 Ok(())
361 }
362}
363
364#[inline]
365fn create_named_field_builder(name_string: Option<&str>) -> proc_macro2::TokenStream {
366 if let Some(name_string) = name_string {
367 quote!(let mut builder = f.debug_struct(#name_string);)
368 } else {
369 super::common::create_debug_map_builder()
370 }
371}