clone_fields/
lib.rs

1//! [![pipeline status](https://gitlab.com/mexus/fields-converter/badges/master/pipeline.svg)](https://gitlab.com/mexus/fields-converter/commits/master)
2//! [![crates.io](https://img.shields.io/crates/v/clone-fields.svg)](https://crates.io/crates/clone-fields)
3//! [![docs.rs](https://docs.rs/clone-fields/badge.svg)](https://docs.rs/clone-fields)
4//!
5//! [[Release docs]](https://docs.rs/clone-fields/)
6//!
7//! [[Master docs]](https://mexus.gitlab.io/clone-fields/clone_fields/)
8//!
9//! Fields-wise types cloning. Nothing more, nothing less.
10//!
11//! ```
12//! use clone_fields::{CloneFields, CloneInto, CloneFrom};
13//!
14//! // PartialEq and Debug traits are only required for `assert` macros in the example.
15//! #[derive(PartialEq, Debug, CloneFields)]
16//! #[destinations("External")]
17//! struct Original<'a, T> {
18//!     field1: &'a i64,
19//!     field2: T,
20//!     nested: OriginalNested,
21//! }
22//!
23//! #[derive(PartialEq, Debug, CloneFields)]
24//! #[destinations("ExternalNested", "ExternalNested2")]
25//! struct OriginalNested {
26//!     value: i32,
27//! }
28//!
29//! // S2 might be a *foreign* type, i.e. declared in a different crate.
30//! struct External<'a, T> {
31//!     field1: &'a i64,
32//!     field2: T,
33//!     nested: ExternalNested,
34//! }
35//!
36//! struct ExternalNested {
37//!     value: i32,
38//! }
39//!
40//! // This struct only exists for the sake of using `destinations` attribute with more than one
41//! // type :)
42//! struct ExternalNested2 {
43//!     value: i32,
44//! }
45//!
46//! fn main() {
47//!     let obj: Original<_> = Original {
48//!         field1: &0,
49//!         field2: String::from("lol"),
50//!         nested: OriginalNested { value: 15 }
51//!     };
52//!     let cloned: External<_> = obj.clone_into();
53//!     assert_eq!(obj.field1, cloned.field1);
54//!     assert_eq!(obj.field2, cloned.field2);
55//!     assert_eq!(obj.nested.value, cloned.nested.value);
56//!     let cloned2 = Original::clone_from(&cloned);
57//!     assert_eq!(cloned.field1, cloned2.field1);
58//!     assert_eq!(cloned.field2, cloned2.field2);
59//!     assert_eq!(obj, cloned2);
60//! }
61//! ```
62
63#![deny(missing_docs)]
64
65extern crate fields_converter_derive;
66
67pub use fields_converter_derive::CloneFields;
68
69/// A trait to clone a type into another type field-by-field.
70///
71/// It is automatically implemented for any type pair for which `CloneFrom` is implemented, i.e.
72/// `T1: CloneFrom<T2>` => `T2: CloneInto<T1>`.
73///
74/// Refer to [clone-fields-derive](https://docs.rs/clone-fields-derive) docs for info on how to
75/// automatically derive this trait. Yes, you typically don't need to do it manually.
76pub trait CloneInto<T> {
77    /// Clones `self` into another type by cloning its fields.
78    fn clone_into(&self) -> T;
79}
80
81/// Construct a type from another by cloning its fields.
82///
83/// It is automatically implemented for any clonable type, i.e. `CloneFrom<T> for T` where
84/// `T: Clone`.
85pub trait CloneFrom<T> {
86    /// Constructs `Self` from another type by cloning its fields.
87    fn clone_from(other: &T) -> Self;
88}
89
90impl<T1, T2> CloneInto<T1> for T2
91where
92    T1: CloneFrom<T2>,
93{
94    fn clone_into(&self) -> T1 {
95        T1::clone_from(self)
96    }
97}
98
99impl<T: Clone> CloneFrom<T> for T {
100    fn clone_from(other: &T) -> T {
101        Clone::clone(other)
102    }
103}
104
105#[cfg(test)]
106mod test {
107    use super::*;
108
109    #[derive(Debug, PartialEq, Clone, CloneFields)]
110    #[destinations("another::S2")]
111    struct S1<'a, T: Clone> {
112        field1: &'a i64,
113        field2: T,
114        field3: Inner1,
115    }
116
117    mod another {
118        use super::*;
119        #[derive(Debug)]
120        pub struct S2<'a, T: Clone> {
121            pub field1: &'a i64,
122            pub field2: T,
123            pub field3: Inner2,
124        }
125    }
126
127    impl<'a, T> PartialEq<another::S2<'a, T>> for S1<'a, T>
128    where
129        T: PartialEq + Clone,
130    {
131        fn eq(&self, other: &another::S2<'a, T>) -> bool {
132            self.field1 == other.field1
133                && self.field2 == other.field2
134                && self.field3 == other.field3
135        }
136    }
137
138    #[derive(Debug, PartialEq, Clone, CloneFields)]
139    #[destinations("Inner2")]
140    struct Inner1 {
141        x: i32,
142    }
143
144    #[derive(Debug, PartialEq)]
145    pub struct Inner2 {
146        x: i32,
147    }
148
149    impl PartialEq<Inner2> for Inner1 {
150        fn eq(&self, other: &Inner2) -> bool {
151            self.x == other.x
152        }
153    }
154
155    #[test]
156    fn check_clone() {
157        let original = S1 {
158            field1: &10,
159            field2: "lol".to_string(),
160            field3: Inner1 { x: 15 },
161        };
162        let cloned: another::S2<_> = CloneInto::clone_into(&original);
163        assert_eq!(original, cloned);
164        let double_cloned: S1<_> = CloneFrom::clone_from(&cloned);
165        assert_eq!(original, double_cloned)
166    }
167}