fields_converter_derive/
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/fields-converter-derive.svg)](https://crates.io/crates/fields-converter-derive)
3//! [![docs.rs](https://docs.rs/fields-converter-derive/badge.svg)](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}