educe/trait_handlers/debug/
debug_struct.rs

1use 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}