clone_fields_derive/
lib.rs

1//! [![pipeline status](https://gitlab.com/mexus/clone-fields/badges/master/pipeline.svg)](https://gitlab.com/mexus/clone-fields/commits/master)
2//! [![crates.io](https://img.shields.io/crates/v/clone-fields-derive.svg)](https://crates.io/crates/clone-fields-derive)
3//! [![docs.rs](https://docs.rs/clone-fields-derive/badge.svg)](https://docs.rs/clone-fields-derive)
4//!
5//! Procedural macro that automatically derives `CloneInto` and `CloneFrom` traits for you.
6
7#[macro_use]
8extern crate quote;
9#[macro_use]
10extern crate syn;
11
12extern crate proc_macro;
13extern crate proc_macro2;
14
15mod input_data;
16mod struct_data;
17
18use self::{input_data::InputData, struct_data::StructData};
19use proc_macro::TokenStream;
20use quote::ToTokens;
21
22/// A derive macro for `CloneInto` and `CloneFrom` traits.
23///
24/// To automagically derive the traits for your type against a `DesiredTypeName` add the
25/// following attributes to it:
26///   * `#[derive(CloneFields)]`,
27///   * and `#[destinations("DesiredTypeName")]`.
28///
29/// ... and the macro will generate an implementations of `CloneInto<DesiredTypeName>` and
30/// `CloneFrom<DesiredTypeName>` for you type then.
31///
32/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
33///
34/// It is possible to use structs with fields with different types, the only requirement is that
35/// respective types should be "clonable" with the `CloneFrom` and `CloneInto` traits.
36///
37/// Please refer to [clone-fields](https://docs.rs/clone-fields) docs for more info on why do you
38/// ;) *implied you need it*
39#[proc_macro_derive(CloneFields, attributes(destinations))]
40pub fn clone_fields_derive(input: TokenStream) -> TokenStream {
41    let InputData {
42        struct_data,
43        destination_types,
44    } = parse_macro_input!(input as InputData);
45    let impls = destination_types
46        .iter()
47        .map(|ty| clone_fields(&struct_data, ty));
48    quote!(
49        #(#impls)*
50        )
51    .into()
52}
53
54/// A derive macro for `Into` and `From` traits, converting the structures field by field.
55///
56/// To automagically derive the traits for your type against a `DesiredTypeName` add the
57/// following attributes to it:
58///   * `#[derive(MoveFields)]`,
59///   * and `#[destinations("DesiredTypeName")]`.
60///
61/// ... and the macro will generate an implementations of `Into<DesiredTypeName>` and
62/// `From<DesiredTypeName>` for you type then.
63///
64/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
65///
66/// It is possible to use structs with fields with different types, the only requirement is that
67/// respective types should be "convertible" with the `From` and `Into` traits.
68#[proc_macro_derive(MoveFields, attributes(destinations))]
69pub fn move_fields_derive(input: TokenStream) -> TokenStream {
70    let InputData {
71        struct_data,
72        destination_types,
73    } = parse_macro_input!(input as InputData);
74    let impls = destination_types
75        .iter()
76        .map(|ty| move_fields(&struct_data, ty));
77    quote!(
78        #(#impls)*
79        )
80    .into()
81}
82
83fn clone_fields(struct_data: &StructData, to_ty: &syn::Path) -> impl ToTokens {
84    let from_ty = &struct_data.name;
85    let fields = &struct_data.field_idents;
86    let (impl_generics, ty_generics, where_clause) = struct_data.generics.split_for_impl();
87    let direct_clones = fields
88        .iter()
89        .map(|field| quote!( #field: CloneInto::clone_into(&self.#field) ));
90    let reverse_clones = fields
91        .iter()
92        .map(|field| quote!( #field: CloneFrom::clone_from(&other.#field) ));
93    quote!(
94        impl #impl_generics CloneInto<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
95            fn clone_into(&self) -> #to_ty #ty_generics {
96                #to_ty {
97                    #( #direct_clones ),*
98                }
99            }
100        }
101
102        impl #impl_generics CloneFrom<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
103            fn clone_from(other: &#to_ty #ty_generics) -> Self {
104                #from_ty {
105                    #( #reverse_clones ),*
106                }
107            }
108        }
109    )
110}
111
112fn move_fields(struct_data: &StructData, to_ty: &syn::Path) -> impl ToTokens {
113    let (impl_generics, ty_generics, where_clause) = struct_data.generics.split_for_impl();
114    let into_fields = struct_data
115        .field_idents
116        .iter()
117        .map(|field| quote!( #field: Into::into(self.#field) ));
118    let from_fields = struct_data
119        .field_idents
120        .iter()
121        .map(|field| quote!( #field: From::from(other.#field) ));
122    let from_ty = &struct_data.name;
123    quote!(
124        impl #impl_generics Into<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
125            fn into(self) -> #to_ty #ty_generics {
126                #to_ty {
127                    #( #into_fields ),*
128                }
129            }
130        }
131
132        impl #impl_generics From<#to_ty #ty_generics> for #from_ty #ty_generics #where_clause {
133            fn from(other: #to_ty #ty_generics) -> Self {
134                #from_ty {
135                    #( #from_fields ),*
136                }
137            }
138        }
139    )
140}