educe/trait_handlers/clone/
clone_struct.rs

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