clone-fields 0.5.0

Fields-wise types cloning
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/clone-fields.svg)](https://crates.io/crates/clone-fields)
//! [![docs.rs](https://docs.rs/clone-fields/badge.svg)](https://docs.rs/clone-fields)
//!
//! [[Release docs]](https://docs.rs/clone-fields/)
//!
//! [[Master docs]](https://mexus.gitlab.io/clone-fields/clone_fields/)
//!
//! Fields-wise types cloning. Nothing more, nothing less.
//!
//! ```
//! use clone_fields::{CloneFields, CloneInto, CloneFrom};
//!
//! // PartialEq and Debug traits are only required for `assert` macros in the example.
//! #[derive(PartialEq, Debug, CloneFields)]
//! #[destinations("External")]
//! struct Original<'a, T> {
//!     field1: &'a i64,
//!     field2: T,
//!     nested: OriginalNested,
//! }
//!
//! #[derive(PartialEq, Debug, CloneFields)]
//! #[destinations("ExternalNested", "ExternalNested2")]
//! struct OriginalNested {
//!     value: i32,
//! }
//!
//! // S2 might be a *foreign* type, i.e. declared in a different crate.
//! struct External<'a, T> {
//!     field1: &'a i64,
//!     field2: T,
//!     nested: ExternalNested,
//! }
//!
//! struct ExternalNested {
//!     value: i32,
//! }
//!
//! // This struct only exists for the sake of using `destinations` attribute with more than one
//! // type :)
//! struct ExternalNested2 {
//!     value: i32,
//! }
//!
//! fn main() {
//!     let obj: Original<_> = Original {
//!         field1: &0,
//!         field2: String::from("lol"),
//!         nested: OriginalNested { value: 15 }
//!     };
//!     let cloned: External<_> = obj.clone_into();
//!     assert_eq!(obj.field1, cloned.field1);
//!     assert_eq!(obj.field2, cloned.field2);
//!     assert_eq!(obj.nested.value, cloned.nested.value);
//!     let cloned2 = Original::clone_from(&cloned);
//!     assert_eq!(cloned.field1, cloned2.field1);
//!     assert_eq!(cloned.field2, cloned2.field2);
//!     assert_eq!(obj, cloned2);
//! }
//! ```

#![deny(missing_docs)]

extern crate fields_converter_derive;

pub use fields_converter_derive::CloneFields;

/// A trait to clone a type into another type field-by-field.
///
/// It is automatically implemented for any type pair for which `CloneFrom` is implemented, i.e.
/// `T1: CloneFrom<T2>` => `T2: CloneInto<T1>`.
///
/// Refer to [clone-fields-derive](https://docs.rs/clone-fields-derive) docs for info on how to
/// automatically derive this trait. Yes, you typically don't need to do it manually.
pub trait CloneInto<T> {
    /// Clones `self` into another type by cloning its fields.
    fn clone_into(&self) -> T;
}

/// Construct a type from another by cloning its fields.
///
/// It is automatically implemented for any clonable type, i.e. `CloneFrom<T> for T` where
/// `T: Clone`.
pub trait CloneFrom<T> {
    /// Constructs `Self` from another type by cloning its fields.
    fn clone_from(other: &T) -> Self;
}

impl<T1, T2> CloneInto<T1> for T2
where
    T1: CloneFrom<T2>,
{
    fn clone_into(&self) -> T1 {
        T1::clone_from(self)
    }
}

impl<T: Clone> CloneFrom<T> for T {
    fn clone_from(other: &T) -> T {
        Clone::clone(other)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[derive(Debug, PartialEq, Clone, CloneFields)]
    #[destinations("another::S2")]
    struct S1<'a, T: Clone> {
        field1: &'a i64,
        field2: T,
        field3: Inner1,
    }

    mod another {
        use super::*;
        #[derive(Debug)]
        pub struct S2<'a, T: Clone> {
            pub field1: &'a i64,
            pub field2: T,
            pub field3: Inner2,
        }
    }

    impl<'a, T> PartialEq<another::S2<'a, T>> for S1<'a, T>
    where
        T: PartialEq + Clone,
    {
        fn eq(&self, other: &another::S2<'a, T>) -> bool {
            self.field1 == other.field1
                && self.field2 == other.field2
                && self.field3 == other.field3
        }
    }

    #[derive(Debug, PartialEq, Clone, CloneFields)]
    #[destinations("Inner2")]
    struct Inner1 {
        x: i32,
    }

    #[derive(Debug, PartialEq)]
    pub struct Inner2 {
        x: i32,
    }

    impl PartialEq<Inner2> for Inner1 {
        fn eq(&self, other: &Inner2) -> bool {
            self.x == other.x
        }
    }

    #[test]
    fn check_clone() {
        let original = S1 {
            field1: &10,
            field2: "lol".to_string(),
            field3: Inner1 { x: 15 },
        };
        let cloned: another::S2<_> = CloneInto::clone_into(&original);
        assert_eq!(original, cloned);
        let double_cloned: S1<_> = CloneFrom::clone_from(&cloned);
        assert_eq!(original, double_cloned)
    }
}