fields_converter_derive/lib.rs
1//! [](https://gitlab.com/mexus/fields-converter/commits/master)
2//! [](https://crates.io/crates/fields-converter-derive)
3//! [](https://docs.rs/fields-converter-derive)
4//!
5//! Collection of procedural macros to allow you "copy", "move" and "duplicate" your structs
6//! fields-wise.
7//!
8//! Here's an ultimate example to give you a feel for what you can do with this crate:
9//!
10//! ```
11//! #[macro_use(Duplicate, MoveFields, CloneFields, EqFields, OrdFields)]
12//! extern crate fields_converter_derive;
13//! extern crate clone_fields;
14//! use clone_fields::{CloneInto, CloneFrom};
15//!
16//! #[derive(Duplicate, MoveFields, CloneFields, EqFields, OrdFields, Debug)]
17//! #[destinations("Copied")]
18//! #[add_derives(Clone, Debug, PartialEq)]
19//! struct Origin<'a, T> {
20//! field1: String,
21//! field2: T,
22//! field3: &'a str,
23//! }
24//!
25//! fn main() {
26//! let source = Origin {
27//! field1: "lol".into(),
28//! field2: 9907,
29//! field3: "testing",
30//! };
31//! // Let's create a `Copied` type from the `Source` (here `CloneFields` shines).
32//! let copied: Copied<_> = source.clone_into();
33//! // Now let's clone it using the `add_derives(Clone)`
34//! let cloned = copied.clone();
35//! // `assert_eq` requires `Debug` and `PartialEq` traits, which are implemented thanks to
36//! // `add_derives(Debug, PartialEq)`.
37//! assert_eq!(copied, cloned);
38//! // .. and compare it to the source object (thanks `EqFields`!).
39//! assert_eq!(source, cloned);
40//! // Ok, let change a field and see that `<` also works (`OrdFields`).
41//! let greater = Copied {
42//! field2: source.field2 + 1,
43//! ..cloned
44//! };
45//! assert!(source < greater);
46//! // ... and vice versa:
47//! assert!(greater > source);
48//! // And, finally, let's move `source` into a `Copied` object, conversion sponsored by
49//! // `MoveFieds`.
50//! let moved: Copied<_> = source.into();
51//! }
52//! ```
53
54#![recursion_limit = "128"]
55
56#[macro_use]
57extern crate quote;
58#[macro_use]
59extern crate syn;
60
61extern crate proc_macro;
62extern crate proc_macro2;
63
64mod clone_fields;
65mod compare_fields;
66mod construct_type;
67mod input_data;
68mod move_fields;
69mod struct_data;
70
71use self::{
72 clone_fields::clone_fields,
73 compare_fields::{eq_fields, ord_fields},
74 construct_type::construct_type,
75 input_data::InputData,
76 move_fields::move_fields,
77};
78use proc_macro::TokenStream;
79
80/// A derive macro for `CloneInto` and `CloneFrom` traits.
81///
82/// To automagically derive the traits for your type against a `DesiredTypeName` add the
83/// following attributes to it:
84/// * `#[derive(CloneFields)]`,
85/// * and `#[destinations("DesiredTypeName")]`.
86///
87/// ... and the macro will generate an implementations of `CloneInto<DesiredTypeName>` and
88/// `CloneFrom<DesiredTypeName>` for you type then.
89///
90/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
91///
92/// It is possible to use structs with fields with different types, the only requirement is that
93/// respective types should be "clonable" with the `CloneFrom` and `CloneInto` traits.
94///
95/// Please refer to [clone-fields](https://docs.rs/clone-fields) docs for more info on why do you
96/// ;) *implied you need it*
97///
98/// ```
99/// #[macro_use(CloneFields)]
100/// extern crate fields_converter_derive;
101/// extern crate clone_fields;
102/// use clone_fields::{CloneInto, CloneFrom};
103///
104/// mod ext {
105/// #[derive(Debug)]
106/// pub struct ExternalType<'a, T> {
107/// pub field1: String,
108/// pub field2: T,
109/// pub field3: &'a str,
110/// }
111/// }
112///
113/// #[derive(CloneFields, Debug)]
114/// #[destinations("ext::ExternalType")]
115/// struct MyType<'a, T> {
116/// field1: String,
117/// field2: T,
118/// field3: &'a str,
119/// }
120///
121/// fn main() {
122/// let source = ext::ExternalType {
123/// field1: "lol".into(),
124/// field2: 9907,
125/// field3: "testing",
126/// };
127/// let my: MyType<_> = source.clone_into();
128/// assert_eq!(my.field1, source.field1);
129/// assert_eq!(my.field2, source.field2);
130/// assert_eq!(my.field3, source.field3);
131/// }
132/// ```
133#[proc_macro_derive(CloneFields, attributes(destinations))]
134pub fn clone_fields_derive(input: TokenStream) -> TokenStream {
135 let InputData {
136 mut struct_data,
137 destination_types,
138 ..
139 } = parse_macro_input!(input as InputData);
140 struct_data.add_bound("Clone");
141 let impls = destination_types
142 .iter()
143 .map(|ty| clone_fields(&struct_data, ty));
144 quote!(#(#impls)*).into()
145}
146
147/// A derive macro for `Into` and `From` traits, converting the structures field by field.
148///
149/// To automagically derive the traits for your type against a `DesiredTypeName` add the
150/// following attributes to it:
151/// * `#[derive(MoveFields)]`,
152/// * and `#[destinations("DesiredTypeName")]`.
153///
154/// ... and the macro will generate an implementations of `Into<DesiredTypeName>` and
155/// `From<DesiredTypeName>` for you type then.
156///
157/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
158///
159/// It is possible to use structs with fields with different types, the only requirement is that
160/// respective types should be "convertible" with the `From` and `Into` traits.
161///
162/// ```
163/// #[macro_use(MoveFields)]
164/// extern crate fields_converter_derive;
165///
166/// #[derive(MoveFields)]
167/// #[destinations("ext::Remote")]
168/// struct Local<'a, T, S: 'a> {
169/// x: T,
170/// y: &'a S,
171/// }
172///
173/// mod ext {
174/// pub struct Remote<'a, T, S: 'a> {
175/// // All the fields of the `Remote` type need to be public since in our derived
176/// // implementations we construct the `Local` type by assigning (and converting)
177/// // each field.
178/// pub x: T,
179/// // Generics and lifetimes are fully supported, fear not!
180/// pub y: &'a S,
181/// }
182/// }
183///
184/// fn main() {
185/// let remote = ext::Remote{x: 14, y: &String::from("wow")};
186/// let local = Local::from(remote);
187/// assert_eq!(local.x, 14);
188/// assert_eq!(local.y, &"wow");
189/// let remote2: ext::Remote<_, _> = local.into();
190/// assert_eq!(remote2.x, 14);
191/// assert_eq!(remote2.y, &"wow");
192/// }
193/// ```
194#[proc_macro_derive(MoveFields, attributes(destinations))]
195pub fn move_fields_derive(input: TokenStream) -> TokenStream {
196 let InputData {
197 struct_data,
198 destination_types,
199 ..
200 } = parse_macro_input!(input as InputData);
201 let impls = destination_types
202 .iter()
203 .map(|ty| move_fields(&struct_data, ty));
204 quote!(#(#impls)*).into()
205}
206
207/// A derive macro to duplicate types.
208///
209/// To automagically derive the traits for your type against a `DesiredTypeName` add the
210/// following attributes to it:
211/// * `#[derive(Duplicate)]`,
212/// * and `#[destinations("DesiredDuplicateName")]`.
213///
214/// You can optionally add a `#[add_derives(Derive1, Derive2, ...)]` attribute to add a
215/// `#[derive(Derive1, Derive2, ...)]` attribute to the generated types.
216///
217/// More than one destination type is supporeted, like `#[destinations("Type1", "Type2", ...)]`.
218///
219/// ```
220/// #[macro_use(Duplicate, MoveFields)]
221/// extern crate fields_converter_derive;
222///
223/// #[derive(Duplicate, MoveFields)]
224/// #[destinations("Copied")]
225/// #[add_derives(Clone, PartialEq, Debug)]
226/// struct Origin<'a, T> {
227/// field1: String,
228/// field2: T,
229/// field3: &'a str,
230/// }
231///
232/// fn main() {
233/// let source = Origin {
234/// field1: "lol".into(),
235/// field2: 9907,
236/// field3: "testing",
237/// };
238/// let copied: Copied<_> = source.into();
239/// let cloned = copied.clone();
240/// assert_eq!(copied, cloned);
241/// }
242/// ```
243#[proc_macro_derive(Duplicate, attributes(destinations, add_derives))]
244pub fn duplicate_derive(input: TokenStream) -> TokenStream {
245 let InputData {
246 struct_data,
247 destination_types,
248 derives,
249 } = parse_macro_input!(input as InputData);
250 let impls = destination_types
251 .iter()
252 .map(|ty| construct_type(&struct_data, ty, &derives));
253 quote!(#(#impls)*).into()
254}
255
256/// A derive macro for `PartialEq` trait.
257///
258/// To automagically derive the trait for your type against a `DesiredTypeName` add the
259/// following attributes to it:
260/// * `#[derive(EqFields)]`,
261/// * and `#[destinations("DesiredTypeName")]`.
262///
263/// ... and the macro will generate an implementations of `PartialEq<DesiredTypeName>` for you type
264/// then.
265///
266/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
267///
268/// It is possible to use structs with fields with different types, the only requirement is that
269/// respective types should be comparable with the `PartialEq` trait.
270/// ```
271/// #[macro_use(EqFields)]
272/// extern crate fields_converter_derive;
273///
274/// #[derive(Debug)]
275/// struct ExternalType<'a, T> {
276/// field1: String,
277/// field2: T,
278/// field3: &'a str,
279/// }
280///
281/// #[derive(EqFields, Debug)]
282/// #[destinations("ExternalType")]
283/// struct MyType<'a, T> {
284/// field1: String,
285/// field2: T,
286/// field3: &'a str,
287/// }
288///
289/// fn main() {
290/// let source = ExternalType {
291/// field1: "lol".into(),
292/// field2: 9907,
293/// field3: "testing",
294/// };
295/// let mut my = MyType {
296/// field1: "lol".into(),
297/// field2: 9907,
298/// field3: "testing",
299/// };
300/// assert_eq!(my, source);
301/// assert_eq!(source, my);
302/// my.field2 += 1;
303/// assert_ne!(source, my);
304/// assert_ne!(my, source);
305/// }
306/// ```
307#[proc_macro_derive(EqFields, attributes(destinations))]
308pub fn eq_fields_derive(input: TokenStream) -> TokenStream {
309 let InputData {
310 mut struct_data,
311 destination_types,
312 ..
313 } = parse_macro_input!(input as InputData);
314 struct_data.add_bound("PartialEq");
315 let impls = destination_types
316 .iter()
317 .map(|ty| eq_fields(&struct_data, ty));
318 quote!(#(#impls)*).into()
319}
320
321/// A derive macro for `PartialOrd` trait.
322///
323/// To automagically derive the trait for your type against a `DesiredTypeName` add the
324/// following attributes to it:
325/// * `#[derive(EqFields, OrdFields)]`,
326/// * and `#[destinations("DesiredTypeName")]`.
327///
328/// ... and the macro will generate an implementations of `PartialEq<DesiredTypeName>` for you type
329/// then. Yes, `EqFields` is a prerequisite for `OrdFields`, the same as `PartialEq` is a
330/// prerequisite for `PartialOrd`.
331///
332/// You can add more than one type, like `#[destinations("Type1", "Type2", ...)]`.
333///
334/// It is possible to use structs with fields with different types, the only requirement is that
335/// respective types should be comparable with the `PartialEq` trait.
336/// ```
337/// #[macro_use(EqFields, OrdFields)]
338/// extern crate fields_converter_derive;
339///
340/// #[derive(Debug)]
341/// struct ExternalType<'a, T> {
342/// field1: String,
343/// field2: T,
344/// field3: &'a str,
345/// }
346///
347/// #[derive(EqFields, OrdFields, Debug)]
348/// #[destinations("ExternalType")]
349/// struct MyType<'a, T: PartialOrd> {
350/// field1: String,
351/// field2: T,
352/// field3: &'a str,
353/// }
354///
355/// fn main() {
356/// let source = ExternalType {
357/// field1: "lol".into(),
358/// field2: 9907,
359/// field3: "testing",
360/// };
361/// let mut my = MyType {
362/// field1: "lol".into(),
363/// field2: 9907,
364/// field3: "testing",
365/// };
366/// assert_eq!(my, source);
367/// assert_eq!(source, my);
368/// assert!(!(my < source));
369/// assert!(!(my > source));
370/// assert!(!(source < my));
371/// assert!(!(source > my));
372/// my.field2 += 1;
373/// assert!(my > source);
374/// assert!(source < my);
375/// my.field1 = "a".into();
376/// assert!(my < source);
377/// assert!(source > my);
378/// }
379/// ```
380#[proc_macro_derive(OrdFields, attributes(destinations))]
381pub fn ord_fields_derive(input: TokenStream) -> TokenStream {
382 let InputData {
383 mut struct_data,
384 destination_types,
385 ..
386 } = parse_macro_input!(input as InputData);
387 struct_data.add_bound("PartialOrd");
388 let impls = destination_types
389 .iter()
390 .map(|ty| ord_fields(&struct_data, ty));
391 quote!(#(#impls)*).into()
392}