educe/trait_handlers/clone/
clone_struct.rs

1use std::{fmt::Write, str::FromStr};
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{punctuated::Punctuated, Data, DeriveInput, Fields, Generics, Meta};
6
7use super::{
8    super::TraitHandler,
9    models::{FieldAttributeBuilder, TypeAttributeBuilder},
10};
11use crate::Trait;
12
13pub struct CloneStructHandler;
14
15impl TraitHandler for CloneStructHandler {
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, enable_bound: true
24        }
25        .from_clone_meta(meta);
26
27        let mut bound = Punctuated::new();
28
29        let mut clone_tokens = TokenStream::new();
30        let mut clone_from_tokens = TokenStream::new();
31
32        if let Data::Struct(data) = &ast.data {
33            let mut field_attributes = Vec::new();
34            let mut field_names = Vec::new();
35
36            #[cfg(feature = "Copy")]
37            let mut has_custom_clone_method = false;
38
39            for (index, field) in data.fields.iter().enumerate() {
40                let field_attribute = FieldAttributeBuilder {
41                    enable_impl: true
42                }
43                .from_attributes(&field.attrs, traits);
44
45                let field_name = if let Some(ident) = field.ident.as_ref() {
46                    ident.to_string()
47                } else {
48                    format!("{}", index)
49                };
50
51                #[cfg(feature = "Copy")]
52                if field_attribute.clone_method.is_some() {
53                    has_custom_clone_method = true;
54                }
55
56                field_attributes.push(field_attribute);
57                field_names.push(field_name);
58            }
59
60            #[cfg(feature = "Copy")]
61            let contains_copy = !has_custom_clone_method && traits.contains(&Trait::Copy);
62
63            #[cfg(not(feature = "Copy"))]
64            let contains_copy = false;
65
66            if contains_copy {
67                bound = type_attribute
68                    .bound
69                    .into_punctuated_where_predicates_by_generic_parameters_with_copy(
70                        &ast.generics.params,
71                    );
72
73                clone_tokens.extend(quote!(*self));
74
75                let mut clone_from = String::new();
76
77                for field_name in field_names {
78                    clone_from
79                        .write_fmt(format_args!(
80                            "core::clone::Clone::clone_from(&mut self.{field_name}, \
81                             &_source.{field_name});",
82                            field_name = field_name
83                        ))
84                        .unwrap();
85                }
86
87                clone_from_tokens.extend(TokenStream::from_str(&clone_from).unwrap());
88            } else {
89                bound = type_attribute
90                    .bound
91                    .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params);
92
93                match &data.fields {
94                    Fields::Unit => {
95                        let ident = &ast.ident;
96
97                        clone_tokens.extend(quote!(#ident));
98                    },
99                    Fields::Unnamed(_) => {
100                        let mut clone = ast.ident.to_string();
101                        let mut clone_from = String::new();
102
103                        clone.push('(');
104
105                        for (index, field_attribute) in field_attributes.into_iter().enumerate() {
106                            let field_name = &field_names[index];
107
108                            let clone_trait = field_attribute.clone_trait;
109                            let clone_method = field_attribute.clone_method;
110
111                            match clone_trait {
112                                Some(clone_trait) => {
113                                    let clone_method = clone_method.unwrap();
114
115                                    clone
116                                        .write_fmt(format_args!(
117                                            "{clone_trait}::{clone_method}(&self.{field_name}),",
118                                            clone_trait = clone_trait,
119                                            clone_method = clone_method,
120                                            field_name = field_name
121                                        ))
122                                        .unwrap();
123                                    clone_from
124                                        .write_fmt(format_args!(
125                                            "self.{field_name} = \
126                                             {clone_trait}::{clone_method}(&_source.{field_name});",
127                                            clone_trait = clone_trait,
128                                            clone_method = clone_method,
129                                            field_name = field_name
130                                        ))
131                                        .unwrap();
132                                },
133                                None => match clone_method {
134                                    Some(clone_method) => {
135                                        clone
136                                            .write_fmt(format_args!(
137                                                "{clone_method}(&self.{field_name}),",
138                                                clone_method = clone_method,
139                                                field_name = field_name
140                                            ))
141                                            .unwrap();
142                                        clone_from
143                                            .write_fmt(format_args!(
144                                                "self.{field_name} = \
145                                                 {clone_method}(&_source.{field_name});",
146                                                clone_method = clone_method,
147                                                field_name = field_name
148                                            ))
149                                            .unwrap();
150                                    },
151                                    None => {
152                                        clone
153                                            .write_fmt(format_args!(
154                                                "core::clone::Clone::clone(&self.{field_name}),",
155                                                field_name = field_name
156                                            ))
157                                            .unwrap();
158                                        clone_from
159                                            .write_fmt(format_args!(
160                                                "core::clone::Clone::clone_from(&mut \
161                                                 self.{field_name}, &_source.{field_name});",
162                                                field_name = field_name
163                                            ))
164                                            .unwrap();
165                                    },
166                                },
167                            }
168                        }
169
170                        clone.push(')');
171
172                        clone_tokens.extend(TokenStream::from_str(&clone).unwrap());
173                        clone_from_tokens.extend(TokenStream::from_str(&clone_from).unwrap());
174                    },
175                    Fields::Named(_) => {
176                        let mut clone = ast.ident.to_string();
177                        let mut clone_from = String::new();
178
179                        clone.push('{');
180
181                        for (index, field_attribute) in field_attributes.into_iter().enumerate() {
182                            let field_name = &field_names[index];
183
184                            let clone_trait = field_attribute.clone_trait;
185                            let clone_method = field_attribute.clone_method;
186
187                            match clone_trait {
188                                Some(clone_trait) => {
189                                    let clone_method = clone_method.unwrap();
190
191                                    clone
192                                        .write_fmt(format_args!(
193                                            "{field_name}: \
194                                             {clone_trait}::{clone_method}(&self.{field_name}),",
195                                            clone_trait = clone_trait,
196                                            clone_method = clone_method,
197                                            field_name = field_name
198                                        ))
199                                        .unwrap();
200                                    clone_from
201                                        .write_fmt(format_args!(
202                                            "self.{field_name} = \
203                                             {clone_trait}::{clone_method}(&_source.{field_name});",
204                                            clone_trait = clone_trait,
205                                            clone_method = clone_method,
206                                            field_name = field_name
207                                        ))
208                                        .unwrap();
209                                },
210                                None => match clone_method {
211                                    Some(clone_method) => {
212                                        clone
213                                            .write_fmt(format_args!(
214                                                "{field_name}: {clone_method}(&self.{field_name}),",
215                                                clone_method = clone_method,
216                                                field_name = field_name
217                                            ))
218                                            .unwrap();
219                                        clone_from
220                                            .write_fmt(format_args!(
221                                                "self.{field_name} = \
222                                                 {clone_method}(&_source.{field_name});",
223                                                clone_method = clone_method,
224                                                field_name = field_name
225                                            ))
226                                            .unwrap();
227                                    },
228                                    None => {
229                                        clone
230                                            .write_fmt(format_args!(
231                                                "{field_name}: \
232                                                 core::clone::Clone::clone(&self.{field_name}),",
233                                                field_name = field_name
234                                            ))
235                                            .unwrap();
236                                        clone_from
237                                            .write_fmt(format_args!(
238                                                "core::clone::Clone::clone_from(&mut \
239                                                 self.{field_name}, &_source.{field_name});",
240                                                field_name = field_name
241                                            ))
242                                            .unwrap();
243                                    },
244                                },
245                            }
246                        }
247
248                        clone.push('}');
249
250                        clone_tokens.extend(TokenStream::from_str(&clone).unwrap());
251                        clone_from_tokens.extend(TokenStream::from_str(&clone_from).unwrap());
252                    },
253                }
254            }
255        }
256
257        let ident = &ast.ident;
258
259        let mut generics_cloned: Generics = ast.generics.clone();
260
261        let where_clause = generics_cloned.make_where_clause();
262
263        for where_predicate in bound {
264            where_clause.predicates.push(where_predicate);
265        }
266
267        let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl();
268
269        let compare_impl = quote! {
270            impl #impl_generics core::clone::Clone for #ident #ty_generics #where_clause {
271                #[inline]
272                fn clone(&self) -> Self {
273                    #clone_tokens
274                }
275
276                #[allow(clippy::incorrect_clone_impl_on_copy_type)]
277                #[inline]
278                fn clone_from(&mut self, _source: &Self) {
279                    #clone_from_tokens
280                }
281            }
282        };
283
284        tokens.extend(compare_impl);
285    }
286}