educe/trait_handlers/clone/
clone_struct.rs1use 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}