educe/trait_handlers/clone/
clone_struct.rs1use quote::quote;
2use syn::{punctuated::Punctuated, Data, DeriveInput, Field, Fields, Index, Meta, Type};
3
4use super::models::{FieldAttribute, FieldAttributeBuilder, TypeAttributeBuilder};
5use crate::{
6 common::where_predicates_bool::WherePredicates, supported_traits::Trait, TraitHandler,
7};
8
9pub(crate) struct CloneStructHandler;
10
11impl TraitHandler for CloneStructHandler {
12 #[inline]
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 type_attribute = TypeAttributeBuilder {
20 enable_flag: true, enable_bound: true
21 }
22 .build_from_clone_meta(meta)?;
23
24 let mut bound: WherePredicates = Punctuated::new();
25
26 let mut clone_token_stream = proc_macro2::TokenStream::new();
27 let mut clone_from_token_stream = proc_macro2::TokenStream::new();
28
29 if let Data::Struct(data) = &ast.data {
30 let mut fields: Vec<(&Field, FieldAttribute)> = Vec::new();
31
32 #[cfg(feature = "Copy")]
33 let contains_copy = traits.contains(&Trait::Copy);
34
35 #[cfg(not(feature = "Copy"))]
36 let contains_copy = false;
37
38 if contains_copy {
39 clone_token_stream.extend(quote!(*self));
40 }
41
42 for field in data.fields.iter() {
43 let field_attribute = FieldAttributeBuilder {
44 enable_method: !contains_copy
45 }
46 .build_from_attributes(&field.attrs, traits)?;
47
48 fields.push((field, field_attribute));
49 }
50
51 let mut clone_types: Vec<&Type> = Vec::new();
52
53 match &data.fields {
54 Fields::Unit => {
55 if !contains_copy {
56 clone_token_stream.extend(quote!(Self));
57 clone_from_token_stream.extend(quote!(let _ = source;));
58 }
59 },
60 Fields::Named(_) => {
61 let mut fields_token_stream = proc_macro2::TokenStream::new();
62 let mut clone_from_body_token_stream = proc_macro2::TokenStream::new();
63
64 if fields.is_empty() {
65 clone_from_body_token_stream.extend(quote!(let _ = source;));
66 } else {
67 for (field, field_attribute) in fields {
68 let field_name = field.ident.as_ref().unwrap();
69
70 if let Some(clone) = field_attribute.method.as_ref() {
71 fields_token_stream.extend(quote! {
72 #field_name: #clone(&self.#field_name),
73 });
74
75 clone_from_body_token_stream.extend(
76 quote!(self.#field_name = #clone(&source.#field_name);),
77 );
78 } else {
79 clone_types.push(&field.ty);
80
81 fields_token_stream.extend(quote! {
82 #field_name: ::core::clone::Clone::clone(&self.#field_name),
83 });
84
85 clone_from_body_token_stream.extend(
86 quote!( ::core::clone::Clone::clone_from(&mut self.#field_name, &source.#field_name); ),
87 );
88 }
89 }
90 }
91
92 if !contains_copy {
93 clone_token_stream.extend(quote! {
94 Self {
95 #fields_token_stream
96 }
97 });
98
99 clone_from_token_stream.extend(clone_from_body_token_stream);
100 }
101 },
102 Fields::Unnamed(_) => {
103 let mut fields_token_stream = proc_macro2::TokenStream::new();
104 let mut clone_from_body_token_stream = proc_macro2::TokenStream::new();
105
106 if fields.is_empty() {
107 clone_from_body_token_stream.extend(quote!(let _ = source;));
108 } else {
109 for (index, (field, field_attribute)) in fields.into_iter().enumerate() {
110 let field_name = Index::from(index);
111
112 if let Some(clone) = field_attribute.method.as_ref() {
113 fields_token_stream.extend(quote!(#clone(&self.#field_name),));
114
115 clone_from_body_token_stream.extend(
116 quote!(self.#field_name = #clone(&source.#field_name);),
117 );
118 } else {
119 clone_types.push(&field.ty);
120
121 fields_token_stream.extend(
122 quote! ( ::core::clone::Clone::clone(&self.#field_name), ),
123 );
124
125 clone_from_body_token_stream.extend(
126 quote!( ::core::clone::Clone::clone_from(&mut self.#field_name, &source.#field_name); ),
127 );
128 }
129 }
130 }
131
132 if !contains_copy {
133 clone_token_stream.extend(quote!(Self ( #fields_token_stream )));
134 clone_from_token_stream.extend(clone_from_body_token_stream);
135 }
136 },
137 }
138
139 bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
140 &ast.generics.params,
141 &syn::parse2(if contains_copy {
142 quote!(::core::marker::Copy)
143 } else {
144 quote!(::core::clone::Clone)
145 })
146 .unwrap(),
147 &clone_types,
148 &[],
149 );
150 }
151
152 let clone_from_fn_token_stream = if clone_from_token_stream.is_empty() {
153 None
154 } else {
155 Some(quote! {
156 #[inline]
157 fn clone_from(&mut self, source: &Self) {
158 #clone_from_token_stream
159 }
160 })
161 };
162
163 let ident = &ast.ident;
164
165 let mut generics = ast.generics.clone();
166 let where_clause = generics.make_where_clause();
167
168 for where_predicate in bound {
169 where_clause.predicates.push(where_predicate);
170 }
171
172 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
173
174 token_stream.extend(quote! {
175 impl #impl_generics ::core::clone::Clone for #ident #ty_generics #where_clause {
176 #[inline]
177 fn clone(&self) -> Self {
178 #clone_token_stream
179 }
180
181 #clone_from_fn_token_stream
182 }
183 });
184
185 #[cfg(feature = "Copy")]
186 if traits.contains(&Trait::Copy) {
187 token_stream.extend(quote! {
188 impl #impl_generics ::core::marker::Copy for #ident #ty_generics #where_clause {
189 }
190 });
191 }
192
193 Ok(())
194 }
195}