clone-fields-derive 0.4.1

Derive macro for CloneFields trait
Documentation
//! [![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 ),*
                }
            }
        }
    )
}