fallacy_clone_derive/
lib.rs

1//! Derive macro crate for fallacy-clone.
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::spanned::Spanned;
6use syn::{Data, Fields};
7
8#[proc_macro_derive(TryClone)]
9pub fn derive_try_clone(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10    let input = TokenStream::from(input);
11    let ast = syn::parse2(input).unwrap();
12    let output = impl_try_clone(ast);
13    output.into()
14}
15
16fn impl_try_clone(ast: syn::DeriveInput) -> TokenStream {
17    let name = ast.ident;
18    let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
19
20    match &ast.data {
21        Data::Struct(data_struct) => {
22            let all_fields = match &data_struct.fields {
23                Fields::Named(fields) => {
24                    let fields = fields.named.iter().map(|field| &field.ident);
25                    quote! {
26                        Self {#( #fields: ::fallacy_clone::TryClone::try_clone(&self.#fields)?, )*}
27                    }
28                }
29                Fields::Unnamed(fields) => {
30                    let fields = (0..fields.unnamed.len()).map(syn::Index::from);
31                    quote! {
32                        Self(#(::fallacy_clone::TryClone::try_clone(&self.#fields)?,)*)
33                    }
34                }
35                Fields::Unit => quote!(Self),
36            };
37
38            quote! {
39                impl #impl_generics ::fallacy_clone::TryClone for #name #type_generics #where_clause {
40                    #[inline]
41                    fn try_clone(&self) -> ::core::result::Result<Self, ::fallacy_clone::AllocError> {
42                        Ok(#all_fields)
43                    }
44                }
45            }
46        }
47        Data::Enum(data_enum) => {
48            let all_variants = data_enum.variants.iter().map(|var| match &var.fields {
49                Fields::Named(fields) => {
50                    let fields = fields.named.iter().map(|field| &field.ident);
51                    let fields2 = fields.clone();
52                    let fields3 = fields.clone();
53                    let variant = &var.ident;
54                    quote! {
55                        #name::#variant{#(#fields,)*} => #name::#variant {
56                            #(#fields2: ::fallacy_clone::TryClone::try_clone(#fields3)?,)*
57                        },
58                    }
59                }
60                Fields::Unnamed(fields) => {
61                    let fields = (0..fields.unnamed.len()).map(|i| {
62                        let mut ident = [0];
63                        syn::Ident::new(((b'a' + i as u8) as char).encode_utf8(&mut ident), var.span())
64                    });
65                    let fields2 = fields.clone();
66                    let variant = &var.ident;
67                    quote! {
68                        #name::#variant(#(#fields,)*) => #name::#variant(
69                            #(::fallacy_clone::TryClone::try_clone(#fields2)?,)*
70                        ),
71                    }
72                }
73                Fields::Unit => {
74                    let variant = &var.ident;
75                    quote! {
76                        #name::#variant => #name::#variant,
77                    }
78                }
79            });
80
81            quote! {
82                impl #impl_generics ::fallacy_clone::TryClone for #name #type_generics #where_clause {
83                    #[inline]
84                    fn try_clone(&self) -> ::core::result::Result<Self, ::fallacy_clone::AllocError> {
85                        Ok(
86                            match self {
87                                #(#all_variants)*
88                            }
89                        )
90                    }
91                }
92            }
93        }
94        Data::Union(_) => panic!("cannot derived TryClone for union"),
95    }
96}