arraygen/
lib.rs

1//!
2//! This crate provides `Arraygen` derive macro for structs, which generates methods returning arrays filled with the selected struct fields.
3//!
4//! Complete example:
5//!
6//! ```rust
7//! use arraygen::Arraygen;
8//!
9//! #[derive(Arraygen, Debug)]
10//! #[gen_array(fn get_names: &mut String)]
11//! struct Person {
12//!     #[in_array(get_names)]
13//!     first_name: String,
14//!     #[in_array(get_names)]
15//!     last_name: String,
16//! }
17//!
18//! let mut person = Person {
19//!     first_name: "Ada".into(),
20//!     last_name: "Lovelace".into()
21//! };
22//!
23//! for name in person.get_names() {
24//!     *name = name.to_lowercase();
25//! }
26//!
27//! assert_eq!(
28//!     format!("{:?}", person),
29//!     "Person { first_name: \"ada\", last_name: \"lovelace\" }"
30//! );
31//! ```
32//!
33//! As you can see above, the attribute `gen_array` generates a new method returning an array of the given type.
34//! And then, the attribute `in_array` indicates the fields to be included by that method. In this case, the
35//! generated method 'get_names' will return an array including references to all the fields of the struct.
36//!
37//! As you might have guessed, what `Arraygen` does under the hood is simply generating the following impl:
38//!
39//! ```rust
40//! # struct Person {
41//! #     first_name: String,
42//! #     last_name: String,
43//! # }
44//! impl Person {
45//!     #[inline(always)]
46//!     fn get_names(&mut self) -> [&mut String; 2] {
47//!         [&mut self.first_name, &mut self.last_name]
48//!     }
49//! }
50//! ```
51
52#![allow(clippy::eval_order_dependence)]
53
54extern crate proc_macro;
55
56use proc_macro::TokenStream;
57
58const DERIVE_NAME: &str = "Arraygen";
59const DECL_FN_NAME: &str = "gen_array";
60const FIELD_SELECTOR_NAME: &str = "in_array";
61const IMPLICIT_SELECT_ALL_NAME: &str = "implicit_select_all";
62
63/// The `Arraygen` derive allows you to use the attribute `gen_array` at the struct level, and the attribute `in_array` in each contained field.
64///
65///
66/// # gen_array
67///
68/// With `gen_array` you can declare your `Arraygen` methods in the following way:
69///
70/// ```ignore
71/// #[gen_array(?visibility fn your_method_name: YourReturnType)]
72/// ```
73///
74/// * **?visibility**: This placeholder is optional. You can let it blank entirely. Or you can write `pub`, `pub(crate)`, or any other pub variant.
75/// * **your_method_name**: This is meant to be any valid method name, following the standard rules. You can't use a name taken by another method in the struct impl. This restriction also includes other `Arraygen` methods.
76/// * **YourReturnType**: The return type can be any Rust type that can appear in a struct field. Notice that if the `type` does not implement the trait `Copy`, you are better returning `&type` or `&mut type` instead, to avoid ownership errors.
77///
78/// There is no limit to the number of methods you can declare.
79///
80/// By default, these new `Arraygen` methods return arrays of length 0. That's not very useful, but that's why we also have the next attribute: `in_array`.
81///
82///
83/// # in_array
84///
85/// With `in_array` you select which field is returned by which method generated by `gen_array`.
86///
87/// ```ignore
88/// #[in_array(your_method_name)]
89/// ```
90///
91/// * `your_method_name`: This needs to match the name of some method declared in the same struct by the `gen_array` attribute.
92///
93///
94/// This is the way to fill up your `Arraygen` methods. The only thing you need to care about is that the type returned by `your_method_name` needs to be compatible with the type of the field with the `in_array` attribute.
95///
96/// Notice that in Rust, non-reference field types can be returned as references, but not the other way around. Or in other words. This is good:
97///
98/// ```rust
99/// # use arraygen::Arraygen;
100/// #[derive(Arraygen)]
101/// #[gen_array(fn references: &i32)]
102/// struct Test {
103///     #[in_array(references)]
104///     data: i32
105/// }
106/// ```
107///
108/// But this is bad:
109///
110/// ```compile_fail
111/// # use arraygen::Arraygen;
112/// #[derive(Arraygen)]
113/// #[gen_array(fn non_references: i32)]
114/// struct Test<'a> {
115///     #[in_array(non_references)]
116///     data: &'a i32
117/// }
118/// ```
119///
120/// Is also good to know that the same field can be included in many `Arraygen` methods, not just in only one.
121/// You will see what I mean by checking the following example:
122///
123/// ```rust
124/// # use arraygen::Arraygen;
125/// #[derive(Arraygen)]
126/// #[gen_array(fn odds: i32)]
127/// #[gen_array(fn evens: i32)]
128/// #[gen_array(fn primes: i32)]
129/// struct Numbers {
130///     #[in_array(odds)]
131///     one: i32,
132///
133///     #[in_array(evens)]
134///     #[in_array(primes)]
135///     two: i32,
136///
137///     #[in_array(odds, primes)] // This syntax is also valid, by the way.
138///     three: i32,
139///
140///     #[in_array(evens)]
141///     four: i32,
142///
143///     #[in_array(odds, primes)]
144///     five: i32
145/// }
146///
147/// let numbers = Numbers {
148///     one: 1,
149///     two: 2,
150///     three: 3,
151///     four: 4,
152///     five: 5
153/// };
154///
155/// assert_eq!(numbers.odds(), [1, 3, 5]);
156/// assert_eq!(numbers.evens(), [2, 4]);
157/// assert_eq!(numbers.primes(), [2, 3, 5]);
158/// ```
159///
160/// Additionally, you may also add decorators to your `in_array` attribute.
161///
162/// ```ignore
163/// #[in_array(your_method_name { comma_separated_decorators })]
164/// ```
165///
166/// Possible decorators are:
167///
168/// * **cast** : This decorator casts the current field to the return type of the `gen_array` method where it will be included.
169/// * **unsafe_transmute** : This one uses [`unsafe { std::mem::transmute }`](https://doc.rust-lang.org/std/mem/fn.transmute.html) to force an unsafe cast of the current field to the return type of the `gen_array` method.
170/// * **override_implicit** : In case the current field is already selected by an `implicit_select_all` clause for this `gen_array` (more about this clause later), you may use `override_implicit` to apply different decorators to the current field.
171///
172/// Casting example:
173///
174/// ```rust
175/// # use arraygen::Arraygen;
176/// #[derive(Arraygen)]
177/// #[gen_array(fn all: i32)]
178/// struct Numbers {
179///     #[in_array(all { cast })]
180///     one: f32,
181///
182///     #[in_array(all { cast })]
183///     two: u8,
184///
185///     #[in_array(all { cast })]
186///     three: bool,
187/// }
188///
189/// let numbers = Numbers {
190///     one: 1.0,
191///     two: 1,
192///     three: true
193/// };
194///
195/// assert_eq!(numbers.all(), [1, 1, 1]);
196///
197/// ```
198///
199///
200///
201/// # Trait Objects
202///
203/// A very good use-case for `Arraygen` consists of extracting [Trait Objects](https://doc.rust-lang.org/reference/types/trait-object.html) from different concrete types, so you can operate in all of them at once.
204///
205/// ```rust
206/// # use arraygen::Arraygen;
207/// trait Animal {
208///     fn talk(&self) -> &'static str;
209/// }
210/// # struct Dog {}
211/// # impl Animal for Dog {
212/// #    fn talk(&self) -> &'static str {
213/// #        "bark"
214/// #     }
215/// # }
216/// # struct Cat {}
217/// # impl Animal for Cat {
218/// #    fn talk(&self) -> &'static str {
219/// #        "meow"
220/// #    }
221/// # }
222/// # struct Pig {}
223/// # impl Animal for Pig {
224/// #    fn talk(&self) -> &'static str {
225/// #        "oink"
226/// #    }
227/// # }
228///
229/// #[derive(Arraygen)]
230/// #[gen_array(fn get_animals: &dyn Animal)]
231/// struct Animals {
232///     #[in_array(get_animals)]
233///     dogo: Dog,
234///     #[in_array(get_animals)]
235///     tiger: Cat,
236///     #[in_array(get_animals)]
237///     bacon: Pig,
238/// }
239///
240/// let animals = Animals {
241///     dogo: Dog {},
242///     tiger: Cat {},
243///     bacon: Pig {}
244/// };
245///
246/// let talk: Vec<&'static str> = animals
247///     .get_animals()
248///     .iter()
249///     .map(|animal| animal.talk())
250///     .collect();
251///
252/// assert_eq!(talk, ["bark", "meow", "oink"]);
253/// ```
254///
255/// And a more realistic example could be this other one:
256///
257/// ```
258/// # use arraygen::Arraygen;
259/// trait SetNone {
260///     fn set_none(&mut self);
261/// }
262///
263/// impl<T> SetNone for Option<T> {
264///     fn set_none(&mut self) {
265///         *self = None;
266///     }
267/// }
268///
269/// #[derive(Arraygen)]
270/// #[gen_array(fn ephemeral_options: &mut dyn SetNone)]
271/// struct ManyOptions {
272///     #[in_array(ephemeral_options)]
273///     a: Option<i32>,
274///     #[in_array(ephemeral_options)]
275///     b: Option<String>,
276///     c: Option<String>,
277/// }
278///
279/// let mut many = ManyOptions {
280///     a: Some(42),
281///     b: Some(String::from("foo")),
282///     c: Some(String::from("bar"))
283/// };
284///
285/// for option in many.ephemeral_options() {
286///     option.set_none();
287/// }
288///
289/// assert_eq!(many.a, None);
290/// assert_eq!(many.b, None);
291/// assert_eq!(many.c, Some(String::from("bar")));
292/// ```
293///
294/// With ad-hoc traits and `Arraygen` is very easy to generalize common transformations with simple one-liners.
295///
296///
297/// # Implicit selection of Fields by their Types
298///
299/// You may omit entirely the `in_array` attribute if you add the `implicit_select_all` clause at the end of your `gen_array` declarations.
300///
301/// ```ignore
302/// #[gen_array(?visibility fn your_method_name: YourReturnType, implicit_select_all: Type1, Type2, Type3)]
303/// ```
304///
305/// You may place either a single type in your `implicit_select_all` clause or a list of comma-separated types.
306/// 
307/// As an example, by adding "`implicit_select_all: f32`" to our `gen_array` method, we'll add all the fields that are of type `f32` in the current `struct`, as shown in the following code:
308///
309/// ```rust
310/// # use arraygen::Arraygen;
311/// #[derive(Arraygen)]
312/// #[gen_array(fn get_all_prices: f32, implicit_select_all: f32)]
313/// struct ImplicitPrices {
314///     pub water: f32,
315///     pub oil: f32,
316///     pub tomato: f32,
317///     pub chocolate: f32,
318/// }
319///
320/// let prices = ImplicitPrices {
321///     water: 2.0,
322///     oil: 4.0,
323///     tomato: 3.0,
324///     chocolate: 5.0,
325/// };
326///
327/// assert_eq!(prices.get_all_prices().iter().sum::<f32>(), 14.0);
328/// ```
329///
330/// The `implicit_select_all` clause may also include decorators:
331/// ```ignore
332/// #[gen_array(?visibility fn your_method_name: YourReturnType, implicit_select_all { comma_separated_decorators }: MatchingFieldTypes)]
333/// ```
334///
335/// See which decorators you may use in the previous `in_array` section.
336///
337/// # Implicit selection of Fields with Type Wildcards
338///
339/// You may use *Type Wildcards* (`_`) on the `implicit_select_all` clause.
340///
341/// For example, the next expression will match all fields regardless of their type (this might be used in conjunction with decorators for casting between types):
342///
343/// ```ignore
344/// #[gen_array(fn all_fields: f32, implicit_select_all: _)]
345/// ```
346///
347/// *Type Wildcards* may also be used within a more complex *Type* definition, like `Option < _ >` or `Result < f32, _ >`.
348/// 
349/// Example:
350///
351/// ```rust
352/// # use arraygen::Arraygen;
353/// #[derive(Arraygen, Debug)]
354/// #[gen_array(fn options: &mut dyn ResetOption, implicit_select_all: Option<_>)]
355/// struct Options {
356///     pub a: Option<i32>,
357///     pub b: Option<bool>
358/// }
359///
360/// impl<T> ResetOption for Option<T> {
361///     fn reset(&mut self) {
362///         *self = None;
363///     }
364/// }
365
366/// trait ResetOption {
367///     fn reset(&mut self);
368/// }
369///
370/// let mut options = Options {
371///     a: Some(1),
372///     b: Some(true)
373/// };
374///
375/// options.options().into_iter().for_each(|o| o.reset());
376/// assert_eq!(format!("{:?}", options), "Options { a: None, b: None }");
377/// ```
378/// 
379/// As you may see above, using *Type Wildcards* in conjuction with [Trait Objects](#trait-objects) allows you to accomplish very powerful constructs in a very succinct manner.
380///
381
382#[proc_macro_derive(Arraygen, attributes(gen_array, in_array))]
383pub fn arraygen(input: TokenStream) -> TokenStream {
384    transform_context::transform_ast(input)
385}
386
387mod parse_attribute;
388mod parse_decorator;
389mod parse_derive_arraygen;
390mod parse_gen_array;
391mod parse_in_array;
392mod transform_context;
393mod types;
394mod utils;