educe/trait_handlers/default/
default_struct.rs

1use std::{fmt::Write, str::FromStr};
2
3use proc_macro2::TokenStream;
4use quote::{quote, ToTokens};
5use syn::{Data, DeriveInput, Fields, Generics, Lit, Meta};
6
7use super::{
8    super::TraitHandler,
9    models::{FieldAttributeBuilder, TypeAttributeBuilder},
10};
11use crate::Trait;
12
13pub struct DefaultStructHandler;
14
15impl TraitHandler for DefaultStructHandler {
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            enable_new:        true,
25            enable_expression: true,
26            enable_bound:      true,
27        }
28        .from_default_meta(meta);
29
30        let bound = type_attribute
31            .bound
32            .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params);
33
34        let mut builder_tokens = TokenStream::new();
35
36        if let Data::Struct(data) = &ast.data {
37            match type_attribute.expression {
38                Some(expression) => {
39                    for field in data.fields.iter() {
40                        let _ = FieldAttributeBuilder {
41                            enable_flag:       false,
42                            enable_literal:    false,
43                            enable_expression: false,
44                        }
45                        .from_attributes(&field.attrs, traits);
46                    }
47
48                    builder_tokens.extend(quote!(#expression));
49                },
50                None => match &data.fields {
51                    Fields::Unit => {
52                        let ident = &ast.ident;
53
54                        builder_tokens.extend(quote!(#ident));
55                    },
56                    Fields::Unnamed(_) => {
57                        let mut struct_tokens = ast.ident.to_string();
58
59                        struct_tokens.push('(');
60
61                        for field in data.fields.iter() {
62                            let field_attribute = FieldAttributeBuilder {
63                                enable_flag:       false,
64                                enable_literal:    true,
65                                enable_expression: true,
66                            }
67                            .from_attributes(&field.attrs, traits);
68
69                            match field_attribute.literal {
70                                Some(value) => match &value {
71                                    Lit::Str(s) => {
72                                        struct_tokens
73                                            .write_fmt(format_args!(
74                                                "core::convert::Into::into({s})",
75                                                s = s.into_token_stream()
76                                            ))
77                                            .unwrap();
78                                    },
79                                    _ => {
80                                        struct_tokens
81                                            .push_str(&value.into_token_stream().to_string());
82                                    },
83                                },
84                                None => match field_attribute.expression {
85                                    Some(expression) => {
86                                        struct_tokens.push_str(&expression);
87                                    },
88                                    None => {
89                                        let typ = field.ty.clone().into_token_stream().to_string();
90
91                                        struct_tokens
92                                            .write_fmt(format_args!(
93                                                "<{typ} as core::default::Default>::default()",
94                                                typ = typ
95                                            ))
96                                            .unwrap();
97                                    },
98                                },
99                            }
100
101                            struct_tokens.push(',');
102                        }
103
104                        struct_tokens.push(')');
105
106                        builder_tokens.extend(TokenStream::from_str(&struct_tokens).unwrap());
107                    },
108                    Fields::Named(_) => {
109                        let mut struct_tokens = ast.ident.to_string();
110
111                        struct_tokens.push('{');
112
113                        for field in data.fields.iter() {
114                            let field_attribute = FieldAttributeBuilder {
115                                enable_flag:       false,
116                                enable_literal:    true,
117                                enable_expression: true,
118                            }
119                            .from_attributes(&field.attrs, traits);
120
121                            let field_name = field.ident.as_ref().unwrap().to_string();
122
123                            struct_tokens
124                                .write_fmt(format_args!("{field_name}: ", field_name = field_name))
125                                .unwrap();
126
127                            match field_attribute.literal {
128                                Some(value) => match &value {
129                                    Lit::Str(s) => {
130                                        struct_tokens
131                                            .write_fmt(format_args!(
132                                                "core::convert::Into::into({s})",
133                                                s = s.into_token_stream()
134                                            ))
135                                            .unwrap();
136                                    },
137                                    _ => {
138                                        struct_tokens
139                                            .push_str(&value.into_token_stream().to_string());
140                                    },
141                                },
142                                None => match field_attribute.expression {
143                                    Some(expression) => {
144                                        struct_tokens.push_str(&expression);
145                                    },
146                                    None => {
147                                        let typ = field.ty.clone().into_token_stream().to_string();
148
149                                        struct_tokens
150                                            .write_fmt(format_args!(
151                                                "<{typ} as core::default::Default>::default()",
152                                                typ = typ
153                                            ))
154                                            .unwrap();
155                                    },
156                                },
157                            }
158
159                            struct_tokens.push(',');
160                        }
161
162                        struct_tokens.push('}');
163
164                        builder_tokens.extend(TokenStream::from_str(&struct_tokens).unwrap());
165                    },
166                },
167            }
168        }
169
170        let ident = &ast.ident;
171
172        let mut generics_cloned: Generics = ast.generics.clone();
173
174        let where_clause = generics_cloned.make_where_clause();
175
176        for where_predicate in bound {
177            where_clause.predicates.push(where_predicate);
178        }
179
180        let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl();
181
182        let default_impl = quote! {
183            impl #impl_generics core::default::Default for #ident #ty_generics #where_clause {
184                #[inline]
185                fn default() -> Self {
186                    #builder_tokens
187                }
188            }
189        };
190
191        tokens.extend(default_impl);
192
193        if type_attribute.new {
194            let new_impl = quote! {
195                impl #impl_generics #ident #ty_generics #where_clause {
196                    /// Returns the "default value" for a type.
197                    #[inline]
198                    pub fn new() -> Self {
199                        <Self as core::default::Default>::default()
200                    }
201                }
202            };
203
204            tokens.extend(new_impl);
205        }
206    }
207}