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}