clone_fields/lib.rs
1//! [](https://gitlab.com/mexus/fields-converter/commits/master)
2//! [](https://crates.io/crates/clone-fields)
3//! [](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}