1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! [![pipeline status](https://gitlab.com/mexus/clone-fields/badges/master/pipeline.svg)](https://gitlab.com/mexus/clone-fields/commits/master)
//! [![crates.io](https://img.shields.io/crates/v/clone-fields-derive.svg)](https://crates.io/crates/clone-fields-derive)
//! [![docs.rs](https://docs.rs/clone-fields-derive/badge.svg)](https://docs.rs/clone-fields-derive)
//!
//! Procedural macro that automatically derives `CloneInto` and `CloneFrom` traits for you.

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod input_data;
mod struct_data;

use self::{input_data::InputData, struct_data::StructData};
use proc_macro::TokenStream;
use quote::ToTokens;

/// A derive macro for `CloneInto` and `CloneFrom` traits.
///
/// To automagically derive the traits for your type against a `DesiredTypeName` add the
/// following attributes to it:
///   * `#[derive(CloneFields)]`,
///   * and `#[destinations("DesiredTypeName")]`.
///
/// ... and the macro will generate an implementations of `CloneInto<DesiredTypeName>` and
/// `CloneFrom<DesiredTypeName>` for you type then.
///
/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
///
/// It is possible to use structs with fields with different types, the only requirement is that
/// respective types should be "clonable" with the `CloneFrom` and `CloneInto` traits.
///
/// Please refer to [clone-fields](https://docs.rs/clone-fields) docs for more info on why do you
/// ;) *implied you need it*
#[proc_macro_derive(CloneFields, attributes(destinations))]
pub fn clone_fields_derive(input: TokenStream) -> TokenStream {
    let InputData {
        struct_data,
        destination_types,
    } = parse_macro_input!(input as InputData);
    let impls = destination_types
        .iter()
        .map(|ty| clone_fields(&struct_data, ty));
    quote!(
        #(#impls)*
        )
    .into()
}

/// A derive macro for `Into` and `From` traits, converting the structures field by field.
///
/// To automagically derive the traits for your type against a `DesiredTypeName` add the
/// following attributes to it:
///   * `#[derive(MoveFields)]`,
///   * and `#[destinations("DesiredTypeName")]`.
///
/// ... and the macro will generate an implementations of `Into<DesiredTypeName>` and
/// `From<DesiredTypeName>` for you type then.
///
/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
///
/// It is possible to use structs with fields with different types, the only requirement is that
/// respective types should be "convertible" with the `From` and `Into` traits.
#[proc_macro_derive(MoveFields, attributes(destinations))]
pub fn move_fields_derive(input: TokenStream) -> TokenStream {
    let InputData {
        struct_data,
        destination_types,
    } = parse_macro_input!(input as InputData);
    let impls = destination_types
        .iter()
        .map(|ty| move_fields(&struct_data, ty));
    quote!(
        #(#impls)*
        )
    .into()
}

fn clone_fields(struct_data: &StructData, to_ty: &syn::Path) -> impl ToTokens {
    let from_ty = &struct_data.name;
    let fields = &struct_data.field_idents;
    let (impl_generics, ty_generics, where_clause) = struct_data.generics.split_for_impl();
    let direct_clones = fields
        .iter()
        .map(|field| quote!( #field: CloneInto::clone_into(&self.#field) ));
    let reverse_clones = fields
        .iter()
        .map(|field| quote!( #field: CloneFrom::clone_from(&other.#field) ));
    quote!(
        impl #impl_generics CloneInto<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
            fn clone_into(&self) -> #to_ty #ty_generics {
                #to_ty {
                    #( #direct_clones ),*
                }
            }
        }

        impl #impl_generics CloneFrom<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
            fn clone_from(other: &#to_ty #ty_generics) -> Self {
                #from_ty {
                    #( #reverse_clones ),*
                }
            }
        }
    )
}

fn move_fields(struct_data: &StructData, to_ty: &syn::Path) -> impl ToTokens {
    let (impl_generics, ty_generics, where_clause) = struct_data.generics.split_for_impl();
    let into_fields = struct_data
        .field_idents
        .iter()
        .map(|field| quote!( #field: Into::into(self.#field) ));
    let from_fields = struct_data
        .field_idents
        .iter()
        .map(|field| quote!( #field: From::from(other.#field) ));
    let from_ty = &struct_data.name;
    quote!(
        impl #impl_generics Into<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
            fn into(self) -> #to_ty #ty_generics {
                #to_ty {
                    #( #into_fields ),*
                }
            }
        }

        impl #impl_generics From<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
            fn from(other: #to_ty #ty_generics) -> Self {
                #from_ty {
                    #( #from_fields ),*
                }
            }
        }
    )
}