fields-converter-derive 0.1.4

Fields-wise type conversions derive macros
Documentation
//! [![pipeline status](https://gitlab.com/mexus/fields-converter/badges/master/pipeline.svg)](https://gitlab.com/mexus/fields-converter/commits/master)
//! [![crates.io](https://img.shields.io/crates/v/fields-converter-derive.svg)](https://crates.io/crates/fields-converter-derive)
//! [![docs.rs](https://docs.rs/fields-converter-derive/badge.svg)](https://docs.rs/fields-converter-derive)
//!
//! Collection of procedural macros to allow you "copy", "move" and "duplicate" your structs
//! fields-wise.
//!
//! Here's an ultimate example to give you a feel for what you can do with this crate:
//!
//! ```
//! #[macro_use(Duplicate, MoveFields, CloneFields, EqFields, OrdFields)]
//! extern crate fields_converter_derive;
//! extern crate clone_fields;
//! use clone_fields::{CloneInto, CloneFrom};
//!
//! #[derive(Duplicate, MoveFields, CloneFields, EqFields, OrdFields, Debug)]
//! #[destinations("Copied")]
//! #[add_derives(Clone, Debug, PartialEq)]
//! struct Origin<'a, T> {
//!   field1: String,
//!   field2: T,
//!   field3: &'a str,
//! }
//!
//! fn main() {
//!   let source = Origin {
//!     field1: "lol".into(),
//!     field2: 9907,
//!     field3: "testing",
//!   };
//!   // Let's create a `Copied` type from the `Source` (here `CloneFields` shines).
//!   let copied: Copied<_> = source.clone_into();
//!   // Now let's clone it using the `add_derives(Clone)`
//!   let cloned = copied.clone();
//!   // `assert_eq` requires `Debug` and `PartialEq` traits, which are implemented thanks to
//!   // `add_derives(Debug, PartialEq)`.
//!   assert_eq!(copied, cloned);
//!   // .. and compare it to the source object (thanks `EqFields`!).
//!   assert_eq!(source, cloned);
//!   // Ok, let change a field and see that `<` also works (`OrdFields`).
//!   let greater = Copied {
//!     field2: source.field2 + 1,
//!     ..cloned
//!   };
//!   assert!(source < greater);
//!   // ... and vice versa:
//!   assert!(greater > source);
//!   // And, finally, let's move `source` into a `Copied` object, conversion sponsored by
//!   // `MoveFieds`.
//!   let moved: Copied<_> = source.into();
//! }
//! ```

#![recursion_limit = "128"]

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

extern crate proc_macro;
extern crate proc_macro2;

mod clone_fields;
mod compare_fields;
mod construct_type;
mod input_data;
mod move_fields;
mod struct_data;

use self::{
    clone_fields::clone_fields,
    compare_fields::{eq_fields, ord_fields},
    construct_type::construct_type,
    input_data::InputData,
    move_fields::move_fields,
};
use proc_macro::TokenStream;

/// 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*
///
/// ```
/// #[macro_use(CloneFields)]
/// extern crate fields_converter_derive;
/// extern crate clone_fields;
/// use clone_fields::{CloneInto, CloneFrom};
///
/// mod ext {
///   #[derive(Debug)]
///   pub struct ExternalType<'a, T> {
///     pub field1: String,
///     pub field2: T,
///     pub field3: &'a str,
///   }
/// }
///
/// #[derive(CloneFields, Debug)]
/// #[destinations("ext::ExternalType")]
/// struct MyType<'a, T> {
///   field1: String,
///   field2: T,
///   field3: &'a str,
/// }
///
/// fn main() {
///   let source = ext::ExternalType {
///     field1: "lol".into(),
///     field2: 9907,
///     field3: "testing",
///   };
///   let my: MyType<_> = source.clone_into();
///   assert_eq!(my.field1, source.field1);
///   assert_eq!(my.field2, source.field2);
///   assert_eq!(my.field3, source.field3);
/// }
/// ```
#[proc_macro_derive(CloneFields, attributes(destinations))]
pub fn clone_fields_derive(input: TokenStream) -> TokenStream {
    let InputData {
        mut struct_data,
        destination_types,
        ..
    } = parse_macro_input!(input as InputData);
    struct_data.add_bound("Clone");
    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.
///
/// ```
/// #[macro_use(MoveFields)]
/// extern crate fields_converter_derive;
///
/// #[derive(MoveFields)]
/// #[destinations("ext::Remote")]
/// struct Local<'a, T, S: 'a> {
///   x: T,
///   y: &'a S,
/// }
///
/// mod ext {
///     pub struct Remote<'a, T, S: 'a> {
///       // All the fields of the `Remote` type need to be public since in our derived
///       // implementations we construct the `Local` type by assigning (and converting)
///       // each field.
///       pub x: T,
///       // Generics and lifetimes are fully supported, fear not!
///       pub y: &'a S,
///     }
/// }
///
/// fn main() {
///   let remote = ext::Remote{x: 14, y: &String::from("wow")};
///   let local = Local::from(remote);
///   assert_eq!(local.x, 14);
///   assert_eq!(local.y, &"wow");
///   let remote2: ext::Remote<_, _> = local.into();
///   assert_eq!(remote2.x, 14);
///   assert_eq!(remote2.y, &"wow");
/// }
/// ```
#[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()
}

/// A derive macro to duplicate types.
///
/// To automagically derive the traits for your type against a `DesiredTypeName` add the
/// following attributes to it:
///   * `#[derive(Duplicate)]`,
///   * and `#[destinations("DesiredDuplicateName")]`.
///
/// You can optionally add a `#[add_derives(Derive1, Derive2, ...)]` attribute to add a
/// `#[derive(Derive1, Derive2, ...)]` attribute to the generated types.
///
/// More than one destination type is supporeted, like `#[destinations("Type1", "Type2", ...)]`.
///
/// ```
/// #[macro_use(Duplicate, MoveFields)]
/// extern crate fields_converter_derive;
///
/// #[derive(Duplicate, MoveFields)]
/// #[destinations("Copied")]
/// #[add_derives(Clone, PartialEq, Debug)]
/// struct Origin<'a, T> {
///   field1: String,
///   field2: T,
///   field3: &'a str,
/// }
///
/// fn main() {
///   let source = Origin {
///     field1: "lol".into(),
///     field2: 9907,
///     field3: "testing",
///   };
///   let copied: Copied<_> = source.into();
///   let cloned = copied.clone();
///   assert_eq!(copied, cloned);
/// }
/// ```
#[proc_macro_derive(Duplicate, attributes(destinations, add_derives))]
pub fn duplicate_derive(input: TokenStream) -> TokenStream {
    let InputData {
        struct_data,
        destination_types,
        derives,
    } = parse_macro_input!(input as InputData);
    let impls = destination_types
        .iter()
        .map(|ty| construct_type(&struct_data, ty, &derives));
    quote!(#(#impls)*).into()
}

/// A derive macro for `PartialEq` trait.
///
/// To automagically derive the trait for your type against a `DesiredTypeName` add the
/// following attributes to it:
///   * `#[derive(EqFields)]`,
///   * and `#[destinations("DesiredTypeName")]`.
///
/// ... and the macro will generate an implementations of `PartialEq<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 comparable with the `PartialEq` trait.
/// ```
/// #[macro_use(EqFields)]
/// extern crate fields_converter_derive;
///
/// #[derive(Debug)]
/// struct ExternalType<'a, T> {
///   field1: String,
///   field2: T,
///   field3: &'a str,
/// }
///
/// #[derive(EqFields, Debug)]
/// #[destinations("ExternalType")]
/// struct MyType<'a, T> {
///   field1: String,
///   field2: T,
///   field3: &'a str,
/// }
///
/// fn main() {
///   let source = ExternalType {
///     field1: "lol".into(),
///     field2: 9907,
///     field3: "testing",
///   };
///   let mut my = MyType {
///     field1: "lol".into(),
///     field2: 9907,
///     field3: "testing",
///   };
///   assert_eq!(my, source);
///   assert_eq!(source, my);
///   my.field2 += 1;
///   assert_ne!(source, my);
///   assert_ne!(my, source);
/// }
/// ```
#[proc_macro_derive(EqFields, attributes(destinations))]
pub fn eq_fields_derive(input: TokenStream) -> TokenStream {
    let InputData {
        mut struct_data,
        destination_types,
        ..
    } = parse_macro_input!(input as InputData);
    struct_data.add_bound("PartialEq");
    let impls = destination_types
        .iter()
        .map(|ty| eq_fields(&struct_data, ty));
    quote!(#(#impls)*).into()
}

/// A derive macro for `PartialOrd` trait.
///
/// To automagically derive the trait for your type against a `DesiredTypeName` add the
/// following attributes to it:
///   * `#[derive(EqFields, OrdFields)]`,
///   * and `#[destinations("DesiredTypeName")]`.
///
/// ... and the macro will generate an implementations of `PartialEq<DesiredTypeName>` for you type
/// then. Yes, `EqFields` is a prerequisite for `OrdFields`, the same as `PartialEq` is a
/// prerequisite for `PartialOrd`.
///
/// 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 comparable with the `PartialEq` trait.
/// ```
/// #[macro_use(EqFields, OrdFields)]
/// extern crate fields_converter_derive;
///
/// #[derive(Debug)]
/// struct ExternalType<'a, T> {
///   field1: String,
///   field2: T,
///   field3: &'a str,
/// }
///
/// #[derive(EqFields, OrdFields, Debug)]
/// #[destinations("ExternalType")]
/// struct MyType<'a, T: PartialOrd> {
///   field1: String,
///   field2: T,
///   field3: &'a str,
/// }
///
/// fn main() {
///   let source = ExternalType {
///     field1: "lol".into(),
///     field2: 9907,
///     field3: "testing",
///   };
///   let mut my = MyType {
///     field1: "lol".into(),
///     field2: 9907,
///     field3: "testing",
///   };
///   assert_eq!(my, source);
///   assert_eq!(source, my);
///   assert!(!(my < source));
///   assert!(!(my > source));
///   assert!(!(source < my));
///   assert!(!(source > my));
///   my.field2 += 1;
///   assert!(my > source);
///   assert!(source < my);
///   my.field1 = "a".into();
///   assert!(my < source);
///   assert!(source > my);
/// }
/// ```
#[proc_macro_derive(OrdFields, attributes(destinations))]
pub fn ord_fields_derive(input: TokenStream) -> TokenStream {
    let InputData {
        mut struct_data,
        destination_types,
        ..
    } = parse_macro_input!(input as InputData);
    struct_data.add_bound("PartialOrd");
    let impls = destination_types
        .iter()
        .map(|ty| ord_fields(&struct_data, ty));
    quote!(#(#impls)*).into()
}