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;