arraygen_docfix/
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().iter_mut() {
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 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//!
45//! impl Person {
46//!     #[inline(always)]
47//!     fn get_names(&mut self) -> [&mut String; 2] {
48//!         [&mut self.first_name, &mut self.last_name]
49//!     }
50//! }
51//! ```
52
53extern crate proc_macro;
54
55use proc_macro::TokenStream;
56use proc_macro2::TokenTree;
57use proc_macro_error::*;
58use quote::quote;
59use std::collections::HashMap;
60
61/// The `Arraygen` derive allows you to use the attribute `gen_array` at the struct level and the attribute `in_array` in each contained field.
62///
63/// With `gen_array` you can declare your `Arraygen` methods in the following way:
64///
65/// `#[gen_array(?visibility fn your_method_name: YourReturnType)]`
66///
67/// * **?visibility** is optional, you can let it blank entirely, or write `pub`, `pub(crate)` and any other pub variant.
68/// * **your_method_name** can be any valid method name. You can't use a name taken by another mehtod in the struct impl, including also other `Arraygen` methods. Otherwise you will get an error.
69/// * **YourReturnType** 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, in order to avoid ownership errors.
70///
71/// There is no limit in the number of methods you can declare.
72///
73/// By default, those new `Arraygen` methods return arrays of length 0. That's not very useful, but that's why we also have the attribute `in_array`.
74///
75/// With `in_array` you indicate which field is returned by which method generated by `gen_array`.
76/// Sintax is the following one:
77///
78/// `#[in_array(your_method_name)]`
79///
80/// * `your_method_name` needs to be some method name generated by `gen_array`.
81///
82/// This is the way to fill up your `Arraygen` methods.
83///
84/// 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.
85/// Notice that non-reference field types can be returned as references, but not the other way around.
86///
87/// Is also good to know that the same field can be included in many `Arraygen` methods, not just in only one.
88/// You will see what I mean by checking the following example:
89///
90/// ```rust
91/// use arraygen::Arraygen;
92///
93/// #[derive(Arraygen)]
94/// #[gen_array(fn odds: i32)]
95/// #[gen_array(fn evens: i32)]
96/// #[gen_array(fn primes: i32)]
97/// struct Numbers {
98///     #[in_array(odds)]
99///     one: i32,
100///
101///     #[in_array(evens)]
102///     #[in_array(primes)]
103///     two: i32,
104///
105///     #[in_array(odds, primes)] // This syntax is also valid, by the way.
106///     three: i32,
107///
108///     #[in_array(evens)]
109///     four: i32,
110///
111///     #[in_array(odds, primes)]
112///     five: i32
113/// }
114///
115/// let numbers = Numbers {
116///     one: 1,
117///     two: 2,
118///     three: 3,
119///     four: 4,
120///     five: 5
121/// };
122///
123/// assert_eq!(numbers.odds(), [1, 3, 5]);
124/// assert_eq!(numbers.evens(), [2, 4]);
125/// assert_eq!(numbers.primes(), [2, 3, 5]);
126/// ```
127///
128/// # Trait Objects
129///
130/// A very good use of `Arraygen` is being able to extract trait objects from different concrete types, so you can operate in all of them at once.
131///
132/// ```rust
133/// use arraygen::Arraygen;
134///
135/// trait Animal {
136///     fn talk(&self) -> &'static str;
137/// }
138///
139/// struct Dog {}
140/// impl Animal for Dog {
141///     fn talk(&self) -> &'static str {
142///         "bark"
143///     }
144/// }
145///
146/// struct Cat {}
147/// impl Animal for Cat {
148///     fn talk(&self) -> &'static str {
149///         "meow"
150///     }
151/// }
152///
153/// #[derive(Arraygen)]
154/// #[gen_array(fn get_animals: &dyn Animal)]
155/// struct Animals {
156///     #[in_array(get_animals)]
157///     dogo: Dog,
158///     #[in_array(get_animals)]
159///     tiger: Cat,
160///     #[in_array(get_animals)]
161///     kitty: Cat,
162/// }
163///
164/// let animals = Animals {
165///     dogo: Dog {},
166///     tiger: Cat {},
167///     kitty: Cat {}
168/// };
169///
170/// let talk: Vec<&'static str> = animals
171///     .get_animals()
172///     .iter()
173///     .map(|animal| animal.talk())
174///     .collect();
175///
176/// assert_eq!(talk, ["bark", "meow", "meow"]);
177/// ```
178///
179/// And a more realistic example could be this other one:
180///
181/// ```
182/// use arraygen::Arraygen;
183///
184/// trait SetNone {
185///     fn set_none(&mut self);
186/// }
187///
188/// impl<T> SetNone for Option<T> {
189///     fn set_none(&mut self) {
190///         *self = None;
191///     }
192/// }
193///
194/// #[derive(Arraygen)]
195/// #[gen_array(fn ephemeral_options: &mut dyn SetNone)]
196/// struct ManyOptions {
197///     #[in_array(ephemeral_options)]
198///     a: Option<i32>,
199///     #[in_array(ephemeral_options)]
200///     b: Option<String>,
201///     c: Option<String>,
202/// }
203///
204/// let mut many = ManyOptions {
205///     a: Some(42),
206///     b: Some(String::from("foo")),
207///     c: Some(String::from("bar"))
208/// };
209///
210/// for option in many.ephemeral_options().iter_mut() {
211///     option.set_none();
212/// }
213///
214/// assert_eq!(many.a, None);
215/// assert_eq!(many.b, None);
216/// assert_eq!(many.c, Some(String::from("bar")));
217/// ```
218///
219/// With ad-hoc traits and `Arraygen` is very easy to generalize common transformations with simple one-liners.
220///
221
222const DERIVE_NAME: &'static str = "Arraygen";
223const DECL_FN_NAME: &'static str = "gen_array";
224const INCLUDE_FIELD: &'static str = "in_array";
225
226#[proc_macro_error]
227#[proc_macro_derive(Arraygen, attributes(gen_array, in_array))]
228pub fn arraygen(input: TokenStream) -> TokenStream {
229    ImplContext::new(input, DERIVE_NAME).transform_ast()
230}
231
232struct ImplContext {
233    ast: syn::DeriveInput,
234    derive_name: &'static str,
235}
236
237impl ImplContext {
238    fn new(input: TokenStream, derive_name: &'static str) -> ImplContext {
239        ImplContext {
240            ast: syn::parse(input).expect("Could not parse AST."),
241            derive_name,
242        }
243    }
244
245    fn transform_ast(&self) -> TokenStream {
246        let mut methods = self
247            .ast
248            .attrs
249            .clone()
250            .into_iter()
251            .flat_map(|attr| {
252                let attribute = attr.clone();
253                attr.path.segments.into_iter().filter_map(move |segment| {
254                    if segment.ident == DECL_FN_NAME {
255                        Some((attribute.clone(), segment.ident.span()))
256                    } else {
257                        None
258                    }
259                })
260            })
261            .map(|(attr, span)| parse_declared_method(attr.tokens, span))
262            .fold(HashMap::new(), |mut acc, method| {
263                if acc.contains_key(&method.name) {
264                    abort!(
265                        method.name.span(),
266                        "{} found two or more methods declared with the name '{}'.",
267                        DECL_FN_NAME,
268                        method.name
269                    )
270                } else {
271                    acc.insert(method.name.clone(), method);
272                    acc
273                }
274            });
275
276        match self.ast.data {
277            syn::Data::Struct(ref class) => read_fields(&class.fields, &mut methods),
278            _ => abort!(
279                self.ast.ident.span(),
280                "The type '{}' is not a struct but tries to derive '{}' which can only be used on structs.",
281                self.ast.ident,
282                self.derive_name
283            ),
284        }
285
286        if methods.len() == 0 {
287            emit_warning!(
288                self.ast.ident.span(),
289                "The type '{}' derives '{}' but does not contain any '{}' attribute, so '{}' does nothing.",
290                self.ast.ident,
291                self.derive_name,
292                DECL_FN_NAME,
293                self.derive_name
294            );
295            return quote! {}.into();
296        }
297
298        let methods =
299            methods
300                .into_iter()
301                .fold(Vec::<TokenTree>::new(), |mut acc, (name, method)| {
302                    if method.fields.len() == 0 {
303                        emit_warning!(
304                            method.name.span(),
305                            "Method '{}' returns an empty array.",
306                            name
307                        );
308                    }
309                    acc.extend(make_method_tokens(&method));
310                    acc
311                });
312
313        let (ty, generics) = (&self.ast.ident, &self.ast.generics);
314        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
315        let tokens = quote! {
316            impl #impl_generics #ty #ty_generics
317                #where_clause
318            {
319                #(#methods)
320                *
321            }
322        };
323        tokens.into()
324    }
325}
326
327#[derive(Debug)]
328struct DeclaredFunction {
329    name: syn::Ident,
330    vis: proc_macro2::TokenStream,
331    ty: proc_macro2::TokenStream,
332    body: proc_macro2::TokenStream,
333    is_mut: bool,
334    is_ref: bool,
335    fields: Vec<syn::Ident>,
336}
337
338enum FunctionParsing {
339    Begin,
340    ExpectingFnOrPubCrate,
341    ExpectingFn,
342    ExpectingName,
343    ExpectingColon,
344    ExpectingArrowEnd,
345    ExpectingType,
346    Error,
347}
348
349fn parse_declared_method(
350    tokens: proc_macro2::TokenStream,
351    gen_array_span: proc_macro2::Span,
352) -> DeclaredFunction {
353    let mut search_element = FunctionParsing::Begin;
354    let mut name: Option<syn::Ident> = None;
355    let mut ty: Vec<TokenTree> = vec![];
356    let mut vis: Vec<TokenTree> = vec![];
357    let mut body: Vec<TokenTree> = vec![];
358    for token in tokens.into_iter() {
359        match token {
360            TokenTree::Group(group) => {
361                for token in group.stream().into_iter() {
362                    if let FunctionParsing::ExpectingType = search_element {
363                        ty.push(token.clone());
364                        continue;
365                    }
366                    match token {
367                        TokenTree::Ident(ref ident) => match search_element {
368                            FunctionParsing::Begin => match ident.to_string().as_ref() {
369                                "pub" => {
370                                    vis.push(token.clone());
371                                    search_element = FunctionParsing::ExpectingFnOrPubCrate;
372                                }
373                                "fn" => {
374                                    body.push(token.clone());
375                                    search_element = FunctionParsing::ExpectingName;
376                                }
377                                _ => search_element = FunctionParsing::Error,
378                            },
379                            FunctionParsing::ExpectingFnOrPubCrate
380                            | FunctionParsing::ExpectingFn => match ident.to_string().as_ref() {
381                                "fn" => {
382                                    body.push(token.clone());
383                                    search_element = FunctionParsing::ExpectingName;
384                                }
385                                _ => search_element = FunctionParsing::Error,
386                            },
387                            FunctionParsing::ExpectingName => {
388                                name = Some(ident.clone());
389                                body.push(token.clone());
390                                search_element = FunctionParsing::ExpectingColon;
391                            }
392                            _ => search_element = FunctionParsing::Error,
393                        },
394                        TokenTree::Group(_) => match search_element {
395                            FunctionParsing::ExpectingFnOrPubCrate => {
396                                vis.push(token.clone());
397                                search_element = FunctionParsing::ExpectingFn;
398                            }
399                            _ => search_element = FunctionParsing::Error,
400                        },
401                        TokenTree::Punct(ref punct) => match search_element {
402                            FunctionParsing::ExpectingArrowEnd => {
403                                if punct.to_string() == ">" {
404                                    search_element = FunctionParsing::ExpectingType;
405                                } else {
406                                    search_element = FunctionParsing::Error;
407                                }
408                            }
409                            FunctionParsing::ExpectingColon => {
410                                if punct.to_string() == ":" {
411                                    search_element = FunctionParsing::ExpectingType;
412                                } else if punct.to_string() == "-" {
413                                    search_element = FunctionParsing::ExpectingArrowEnd;
414                                } else {
415                                    search_element = FunctionParsing::Error
416                                }
417                            }
418                            _ => search_element = FunctionParsing::Error,
419                        },
420                        _ => search_element = FunctionParsing::Error,
421                    }
422                }
423            }
424            _ => search_element = FunctionParsing::Error,
425        }
426    }
427    if ty.len() == 0 {
428        search_element = FunctionParsing::Error;
429    }
430    let is_ref = ty.len() >= 1 && ty[0].to_string() == "&";
431    let is_mut = is_ref && ty.len() >= 2 && ty[1].to_string() == "mut";
432    let decl_fn = if let Some(name) = name {
433        Some(DeclaredFunction {
434            name,
435            vis: vis.into_iter().collect(),
436            ty: ty.into_iter().collect(),
437            body: body.into_iter().collect(),
438            is_mut,
439            is_ref,
440            fields: vec![],
441        })
442    } else {
443        None
444    };
445    match search_element {
446        FunctionParsing::ExpectingType => {
447            if let Some(decl_fn) = decl_fn {
448                return decl_fn;
449            }
450        }
451        FunctionParsing::Error => {
452            if let Some(decl_fn) = decl_fn {
453                abort!(decl_fn.name.span(), "'{}' tried to declare a method '{}', but the return type syntax was wrong.", DECL_FN_NAME, decl_fn.name;
454                    help = "Correct syntax is {}", decl_fn_correct_syntax(&decl_fn););
455            } else {
456                abort!(gen_array_span, "'{}' was used with the wrong syntax.", DECL_FN_NAME;
457                    help = "Correct syntax is {}", decl_fn_correct_syntax_without_name());
458            }
459        }
460        _ => {}
461    }
462    abort!(
463        gen_array_span,
464        "Bug on '{}', contact with the maintainer of {} crate.",
465        DECL_FN_NAME,
466        DERIVE_NAME
467    );
468}
469
470fn read_fields(fields: &syn::Fields, methods: &mut HashMap<syn::Ident, DeclaredFunction>) {
471    for field in fields.iter() {
472        if field.attrs.is_empty() {
473            continue;
474        }
475        if let Some(ref ident) = field.ident {
476            for attr in field.attrs.iter() {
477                let segments: Vec<_> = attr
478                    .path
479                    .segments
480                    .iter()
481                    .filter_map(|segment| {
482                        if segment.ident == INCLUDE_FIELD {
483                            Some(segment.ident.clone())
484                        } else {
485                            None
486                        }
487                    })
488                    .collect();
489                let include_ident = match segments.len() {
490                    0 => continue,
491                    1 => &segments[0],
492                    // @TODO Not sure if this condition can actually happen, not covered in tests yet.
493                    _ => abort!(
494                        segments[0].span(),
495                        "Wrong syntax, used multiple '{}' in same attribute.",
496                        INCLUDE_FIELD
497                    ),
498                };
499                let mut error = false;
500                let mut correct_fns = vec![];
501                let mut need_comma = false;
502                for token in attr.tokens.clone() {
503                    match token {
504                        TokenTree::Group(group) => {
505                            for token in group.stream().into_iter() {
506                                match token {
507                                    TokenTree::Ident(name) => {
508                                        if need_comma {
509                                            error = true;
510                                        }
511                                        match methods.get_mut(&name) {
512                                            Some(ref mut method) => {
513                                                let has_this_field_already = method.fields.iter().any(|field| field == ident);
514                                                if has_this_field_already {
515                                                    abort!(include_ident.span(), "Field '{}' is already included in method '{}', no need to repeat it.", ident, name;
516                                                        help = "Remove the repeated entries.");
517                                                }
518                                                method.fields.push(ident.clone());
519                                                need_comma = true;
520                                            }
521                                            None => error = true,
522                                        }
523                                        correct_fns.push(name.clone());
524                                    }
525                                    TokenTree::Punct(punct) => {
526                                        if need_comma && punct.to_string() == "," {
527                                            need_comma = false;
528                                        } else {
529                                            error = true;
530                                        }
531                                    }
532                                    _ => error = true,
533                                }
534                            }
535                        }
536                        _ => error = true,
537                    }
538                }
539                if error {
540                    if correct_fns.len() > 0 {
541                        for correct_fn in &correct_fns {
542                            if let None = methods.get_mut(&correct_fn) {
543                                abort!(correct_fn.span(), "Method '{}' was not declared with the attribute '{}' at struct level.", correct_fn, DECL_FN_NAME);
544                            }
545                        }
546                        let correct_fns = correct_fns
547                            .iter()
548                            .map(|ident| ident.to_string())
549                            .collect::<Vec<String>>()
550                            .join(", ");
551                        abort!(include_ident.span(), "'{}' shouldn't contain those tokens.", INCLUDE_FIELD;
552                            help = "Correct syntax is {}", include_field_correct_syntax(&correct_fns));
553                    } else {
554                        abort!(include_ident.span(), "'{}' was used with the wrong syntax.", INCLUDE_FIELD;
555                            help = "Correct syntax is {}", include_field_correct_syntax_without_name());
556                    }
557                }
558            }
559        }
560    }
561}
562
563fn make_method_tokens(props: &DeclaredFunction) -> proc_macro2::TokenStream {
564    let field_idents = &props.fields;
565    let count = field_idents.len();
566    let return_type = &props.ty;
567    let vis = &props.vis;
568    let body = &props.body;
569    let refa = if props.is_ref {
570        if props.is_mut {
571            quote! {&mut}
572        } else {
573            quote! {&}
574        }
575    } else {
576        quote! {}
577    };
578    let muta = if props.is_mut {
579        quote! {mut}
580    } else {
581        quote! {}
582    };
583    quote! {
584        #[inline(always)]
585        #vis #body (& #muta self) -> [#return_type; #count] {
586            [#(#refa self.#field_idents),*]
587        }
588    }
589}
590
591fn decl_fn_correct_syntax_without_name() -> String {
592    format!("#[{}(fn your_function_name: YourReturnType)]", DECL_FN_NAME)
593}
594
595fn decl_fn_correct_syntax(decl_fn: &DeclaredFunction) -> String {
596    let vis = &decl_fn.vis;
597    let body = &decl_fn.body;
598    let signature = quote! {
599        #vis #body: YourReturnType
600    };
601    format!("#[{}({})]", DECL_FN_NAME, signature.to_string())
602}
603
604fn include_field_correct_syntax_without_name() -> String {
605    format!("#[{}(your_generated_function_name)]", INCLUDE_FIELD)
606}
607
608fn include_field_correct_syntax(correct_fns: &String) -> String {
609    format!("#[{}({})]", INCLUDE_FIELD, correct_fns)
610}