getters_by_type/lib.rs
1//!
2//! This crate provides [`GettersByType`](derive.GettersByType.html) derive macro for structs, which implements a getter method for each type they contain.
3//!
4//! The generated methods start with the prefix `get_fields_` and end with a transcription of the type they refer.
5//!
6//! Example using `GettersByType` :
7//!
8//! ```rust
9//! use getters_by_type::GettersByType;
10//! #[derive(GettersByType)]
11//! struct Foo {
12//! first: i32,
13//! second: i32,
14//! third: i32,
15//! }
16//!
17//! let object = Foo { first: 6, second: 12, third: 24 };
18//!
19//! // Let's sum all the i32 fields with a fold expression:
20//! assert_eq!(object.get_fields_i32().iter().fold(0, |acc, x| **x + acc), 42);
21//! ```
22//!
23//! As you notice, the getter methods return an array containing references to all the fields of the same type.
24//! In that example, the return type of the method `get_fields_i32` would be `[&i32; 3]`.
25//!
26//! This crate also provides a `mut` version [`GettersMutByType`](derive.GettersMutByType.html) which also adds a mut version for those methods.
27//!
28//! In this case, the generated methods start with the prefix `get_mut_fields_` instead.
29//!
30//! Example using `GettersMutByType` :
31//!
32//!
33//! ```rust
34//! use getters_by_type::GettersMutByType;
35//!
36//! #[derive(Default)]
37//! struct Updater {}
38//! impl Updater {
39//! fn update(&mut self) {/*...*/}
40//! }
41//!
42//! #[derive(GettersMutByType, Default)]
43//! struct Foo {
44//! first: Updater,
45//! second: Updater,
46//! /*...*/
47//! onehundredth: Updater,
48//! }
49//!
50//! let mut object = Foo::default();
51//!
52//! // Let's update all the Updater fields
53//! for updater in object.get_mut_fields_updater().iter_mut() {
54//! updater.update();
55//! }
56//! ```
57//!
58//! In this example, the return type of the method `get_mut_fields_updater` would be `[&mut Updater; 3]`.
59//! There is no dynamic memory allocation happening within the getter methods, as they just return a fixed array with references.
60//! There isn't also unsafe code being generated.
61//! For more documentation and examples, see each respective documentation section.
62
63extern crate proc_macro;
64
65use proc_macro::TokenStream;
66use proc_macro2::{Span, TokenTree};
67use quote::quote;
68use std::collections::HashMap;
69
70/// The `GettersByType` macro automatically generates an `impl` for the given struct,
71/// implementing a getter method for each different type contained within the struct.
72///
73/// The generated methods start with the prefix `get_fields_` and end with a transcription of the type they refer.
74///
75/// Example:
76///
77/// ```rust
78/// use getters_by_type::GettersByType;
79/// #[derive(GettersByType)]
80/// struct Foo {
81/// a: String,
82/// b: String,
83/// }
84///
85/// // Would generete:
86///
87/// impl Foo {
88/// fn get_fields_string(&self) -> [&String; 2] {
89/// [&self.a, &self.b]
90/// }
91/// }
92/// ```
93///
94/// As you notice, the chars of all the types (`String` in this case) go
95/// to the method signature in lowercase form.
96///
97/// It works the same with generic, reference and other types, with the following exceptions:
98/// 1. Characters `<` `>` `(` `)` `[` `]` `,` `;` always get converted to `_`.
99/// 2. Return type arrow `->` and reference character `&` get ignored completely.
100/// 3. Pointer types `*const` and `*mut` get converted o `ptr_const_` and `ptr_mut_` respectively.
101///
102/// Also, reference types and non-reference types will be covered by the same
103/// method, as the methods are always returning references in the first place.
104///
105/// Example for fn, generic and reference types (more examples for other types later):
106///
107/// ```rust
108/// use getters_by_type::GettersByType;
109/// #[derive(GettersByType)]
110/// struct Foo {
111/// a: Result<i32, i32>,
112/// b: Result<i32, Result<i32, i32>>,
113/// c: fn(usize) -> f32,
114/// d: &'static bool,
115/// e: bool,
116/// }
117///
118/// // Would generate:
119///
120/// impl Foo {
121/// fn get_fields_result_i32_i32_(&self) -> [&Result<i32, i32>; 1] {
122/// [&self.a]
123/// }
124/// fn get_fields_result_i32_result_i32_i32__(&self) -> [&Result<i32, Result<i32, i32>>; 1] {
125/// [&self.b]
126/// }
127/// fn get_fields_fn_usize___f32(&self) -> [&fn(usize) -> f32; 1] {
128/// [&self.c]
129/// }
130/// fn get_fields_bool(&self) -> [&bool; 2] {
131/// [&self.d, &self.e]
132/// }
133/// }
134/// ```
135///
136/// Other examples of types to method signature conversions here:
137///
138/// ```rust
139/// use getters_by_type::GettersByType;
140/// #[derive(GettersByType)]
141/// struct Foo<'a> {
142/// a: &'a str,
143/// b: Box<Fn(i32) -> f32>,
144/// c: &'a Vec<&'a Option<&'a i32>>,
145/// d: Result<i32, Result<i32, Result<i32, &'static str>>>,
146/// e: Option<Option<Option<Option<Option<fn(usize)>>>>>,
147/// f: (i32, i32),
148/// g: [i32; 2],
149/// h: &'a [&'a Option<&'a i32>],
150/// i: *const i32,
151/// j: *mut i32,
152/// k: Box<dyn Bar>,
153/// }
154/// trait Bar {}
155/// impl Bar for i32 {}
156/// let vector = vec!();
157/// let number_1 = 1;
158/// let mut number_2 = 2;
159/// let o = Foo {
160/// a: "",
161/// b: Box::new(|_| 0.0),
162/// c: &vector,
163/// d: Ok(0),
164/// e: None,
165/// f: (0, 0),
166/// g: [0, 0],
167/// h: vector.as_slice(),
168/// i: &number_1,
169/// j: &mut number_2,
170/// k: Box::new(0),
171/// };
172/// // from type: &'a str
173/// o.get_fields_str();
174/// // from type: Box<Fn(i32) -> f32>
175/// o.get_fields_box_fn_i32_f32_();
176/// // from type: &'a Vec<&'a Option<&'a i32>>
177/// o.get_fields_vec_option_i32__();
178/// // from type: Result<i32, Result<i32, Result<i32, &'static str>>>
179/// o.get_fields_result_i32_result_i32_result_i32_str___();
180/// // from type: Option<Option<Option<Option<Option<fn(usize)>>>>>
181/// o.get_fields_option_option_option_option_option_fn_usize______();
182/// // from type: (i32, i32)
183/// o.get_fields__i32_i32_();
184/// // from type: [i32; 2]
185/// o.get_fields__i32_2_();
186/// // from type: &'a [&'a Option<&'a i32>]
187/// o.get_fields__option_i32__();
188/// // from type: *const i32
189/// o.get_fields_ptr_const_i32();
190/// // from type: *mut i32
191/// o.get_fields_ptr_mut_i32();
192/// // from type: Box<dyn Bar>
193/// o.get_fields_box_dyn_bar_();
194/// ```
195///
196/// Method visibility is inherited directly from the struct visibility,
197/// so if the struct is public, all the methods generated by `GettersByType`
198/// will be public too. There is no fine-grained control for fields visibility.
199///
200/// There are still some types not implemented. Those are the following:
201///
202/// * `TraitObject` is partially implemented, `Box<dyn Trait>` works, but `&dyn Trait` doesn't.
203/// * `Never`
204/// * `ImplTrait`
205/// * `Group`
206/// * `Infer`
207/// * `Macro`
208/// * `Verbatim`
209///
210/// Hopefully, they will get implemented in next releases.
211///
212#[proc_macro_derive(GettersByType)]
213pub fn getters_by_type(input: TokenStream) -> TokenStream {
214 ImplContext::new(input, "GettersByType", false).transform_ast()
215}
216
217/// The `GettersMutByType` macro automatically generates an `impl` for the given struct,
218/// implementing a getter method for each different type contained within the struct.
219///
220/// The generated methods start with the prefix `get_mut_fields_` and end with a transcription of the type they refer.
221///
222/// Example:
223///
224/// ```rust
225/// use getters_by_type::GettersMutByType;
226/// #[derive(GettersMutByType)]
227/// struct Foo {
228/// a: String,
229/// b: String,
230/// }
231///
232/// // Would generete:
233///
234/// impl Foo {
235/// fn get_mut_fields_string(&mut self) -> [&mut String; 2] {
236/// [&mut self.a, &mut self.b]
237/// }
238/// }
239/// ```
240///
241/// This is the mutable version of `GettersByType`.
242/// The same rules are applying, so check the [GettersByType derive](derive.GettersByType.html) documentation first.
243///
244/// There is one important difference, thought. There are some fields with types that
245/// can't be made mutable. I.e. types with immutable references. When that's the case,
246/// the field gets ignored completely.
247///
248/// Example:
249///
250/// ```rust
251/// use getters_by_type::GettersMutByType;
252/// #[derive(GettersMutByType)]
253/// struct Foo<'a> {
254/// a: &'a String,
255/// b: String,
256/// }
257///
258/// let string = String::new();
259/// let mut o = Foo {
260/// a: &string,
261/// b: "".into(),
262/// };
263///
264/// assert_eq!(o.get_mut_fields_string().len(), 1); // instead of 2
265/// ```
266#[proc_macro_derive(GettersMutByType)]
267pub fn getters_mut_by_type(input: TokenStream) -> TokenStream {
268 ImplContext::new(input, "GettersByMutType", true).transform_ast()
269}
270
271struct ImplContext {
272 ast: syn::DeriveInput,
273 derive_name: &'static str,
274 with_mutability: bool,
275}
276
277impl ImplContext {
278 fn new(input: TokenStream, derive_name: &'static str, with_mutability: bool) -> ImplContext {
279 ImplContext {
280 ast: syn::parse(input).expect("Could not parse AST."),
281 derive_name,
282 with_mutability,
283 }
284 }
285
286 fn transform_ast(&self) -> TokenStream {
287 let fields_by_type = match self.ast.data {
288 syn::Data::Struct(ref class) => self.read_fields(&class.fields),
289 _ => panic!(
290 "The type '{}' is not a struct but tries to derive '{}' which can only be used on structs.",
291 self.ast.ident, self.derive_name
292 ),
293 };
294 let mut methods = Vec::<TokenTree>::new();
295 for (type_pieces, fields_sharing_type) in fields_by_type.into_iter() {
296 let return_type = MethodReturnType {
297 ty: fields_sharing_type.ty,
298 name: make_type_name_from_type_pieces(type_pieces),
299 };
300 methods.extend(self.make_method_tokens("get_fields", &return_type, false, fields_sharing_type.immutable_fields));
301 if self.with_mutability {
302 methods.extend(self.make_method_tokens("get_mut_fields", &return_type, true, fields_sharing_type.mutable_fields));
303 }
304 }
305 let (ty, generics) = (&self.ast.ident, &self.ast.generics);
306 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
307 let tokens = quote! {
308 impl #impl_generics #ty #ty_generics
309 #where_clause
310 {
311 #(#methods)
312 *
313 }
314 };
315 tokens.into()
316 }
317
318 fn read_fields<'a>(&self, fields: &'a syn::Fields) -> HashMap<Vec<TypePart<'a>>, FieldsSharingType<'a>> {
319 let mut fields_by_type = HashMap::<Vec<TypePart>, FieldsSharingType>::new();
320 for field in fields.iter() {
321 if let Some(ref ident) = field.ident {
322 let info = get_info_from_type(&field.ty);
323 match make_idents_from_type(&field.ty) {
324 Ok(type_pieces) => {
325 let fields_by_type = fields_by_type.entry(type_pieces).or_insert_with(|| FieldsSharingType::new(info.ty));
326 if info.is_mutable && self.with_mutability {
327 fields_by_type.mutable_fields.push(ident);
328 }
329 fields_by_type.immutable_fields.push(ident);
330 }
331 Err(err) => {
332 eprintln!("[WARNING::{}] Field '{}' of struct '{}' not covered because: {}", self.derive_name, ident, self.ast.ident, err);
333 }
334 }
335 }
336 }
337 fields_by_type
338 }
339
340 fn make_method_tokens(&self, method_prefix: &str, return_type: &MethodReturnType, mutability: bool, field_idents: Vec<&syn::Ident>) -> proc_macro2::TokenStream {
341 let count = field_idents.len();
342 let method_name = syn::Ident::new(&format!("{}_{}", method_prefix, return_type.name), Span::call_site());
343 let (vis, return_type) = (&self.ast.vis, &return_type.ty);
344 if mutability {
345 quote! {
346 #vis fn #method_name(&mut self) -> [&mut #return_type; #count] {
347 [#(&mut self.#field_idents),*]
348 }
349 }
350 } else {
351 quote! {
352 #vis fn #method_name(&self) -> [&#return_type; #count] {
353 [#(&self.#field_idents),*]
354 }
355 }
356 }
357 }
358}
359
360struct MethodReturnType<'a> {
361 ty: &'a syn::Type,
362 name: String,
363}
364
365struct FieldsSharingType<'a> {
366 immutable_fields: Vec<&'a syn::Ident>,
367 mutable_fields: Vec<&'a syn::Ident>,
368 ty: &'a syn::Type,
369}
370
371impl<'a> FieldsSharingType<'a> {
372 fn new(ty: &'a syn::Type) -> FieldsSharingType {
373 FieldsSharingType {
374 immutable_fields: vec![],
375 mutable_fields: vec![],
376 ty,
377 }
378 }
379}
380
381struct TypeInfo<'a> {
382 is_mutable: bool,
383 ty: &'a syn::Type,
384}
385
386#[derive(Hash, PartialEq, Eq)]
387enum TypePart<'a> {
388 Ident(&'a syn::Ident),
389 Integer(u64),
390 Separator(&'static str),
391}
392
393impl<'a> TypePart<'a> {
394 fn to_string(&self) -> String {
395 match self {
396 TypePart::Ident(i) => i.to_string(),
397 TypePart::Separator(s) => s.to_string(),
398 TypePart::Integer(i) => i.to_string(),
399 }
400 }
401}
402
403fn get_info_from_type(ty: &syn::Type) -> TypeInfo {
404 let (ty, is_mutable) = match ty {
405 syn::Type::Reference(ref reference) => (&*reference.elem, reference.mutability.is_some()),
406 _ => (ty, true),
407 };
408 TypeInfo { is_mutable, ty }
409}
410
411fn make_idents_from_type<'a>(ty: &'a syn::Type) -> Result<Vec<TypePart<'a>>, &'static str> {
412 let mut type_pieces = Vec::<TypePart<'a>>::with_capacity(8);
413 fill_type_pieces_from_type(&mut type_pieces, ty)?;
414 Ok(type_pieces)
415}
416
417fn fill_type_pieces_from_type<'a>(type_pieces: &mut Vec<TypePart<'a>>, ty: &'a syn::Type) -> Result<(), &'static str> {
418 match ty {
419 syn::Type::Path(ref path) => fill_type_pieces_from_type_path(type_pieces, &path.path),
420 syn::Type::Reference(ref reference) => fill_type_pieces_from_type(type_pieces, &reference.elem),
421 syn::Type::BareFn(ref function) => {
422 type_pieces.push(TypePart::Separator("fn("));
423 fill_type_pieces_from_array_of_inputs(type_pieces, &function.inputs, ",", |type_pieces, arg| fill_type_pieces_from_type(type_pieces, &arg.ty))?;
424 type_pieces.push(TypePart::Separator(")"));
425 fill_type_pieces_from_return_type(type_pieces, &function.output)?;
426 Ok(())
427 }
428 syn::Type::Slice(slice) => {
429 type_pieces.push(TypePart::Separator("["));
430 fill_type_pieces_from_type(type_pieces, &slice.elem)?;
431 type_pieces.push(TypePart::Separator("]"));
432 Ok(())
433 }
434 syn::Type::Array(array) => {
435 type_pieces.push(TypePart::Separator("["));
436 fill_type_pieces_from_type(type_pieces, &array.elem)?;
437 type_pieces.push(TypePart::Separator(";"));
438 match &array.len {
439 syn::Expr::Lit(lit) => match &lit.lit {
440 syn::Lit::Int(int) => type_pieces.push(TypePart::Integer(int.value())),
441 _ => return Err("syn::Lit::* are not implemented yet."),
442 },
443 _ => return Err("syn::Expr::* are not implemented yet."),
444 }
445 type_pieces.push(TypePart::Separator("]"));
446 Ok(())
447 }
448 syn::Type::Tuple(tuple) => {
449 type_pieces.push(TypePart::Separator("("));
450 fill_type_pieces_from_array_of_inputs(type_pieces, &tuple.elems, ",", fill_type_pieces_from_type)?;
451 type_pieces.push(TypePart::Separator(")"));
452 Ok(())
453 }
454 syn::Type::Paren(paren) => {
455 type_pieces.push(TypePart::Separator("("));
456 fill_type_pieces_from_type(type_pieces, &paren.elem)?;
457 type_pieces.push(TypePart::Separator(")"));
458 Ok(())
459 }
460 syn::Type::Ptr(ptr) => {
461 type_pieces.push(TypePart::Separator("ptr_"));
462 if ptr.const_token.is_some() {
463 type_pieces.push(TypePart::Separator("const_"));
464 }
465 if ptr.mutability.is_some() {
466 type_pieces.push(TypePart::Separator("mut_"));
467 }
468 fill_type_pieces_from_type(type_pieces, &ptr.elem)?;
469 Ok(())
470 }
471 syn::Type::ImplTrait(_) => Err("syn::Type::ImplTrait can not be implemented."), // ImplTrait is not valid outside of functions and inherent return types, so can't be implemented.
472 syn::Type::TraitObject(trait_object) => {
473 if trait_object.dyn_token.is_some() {
474 type_pieces.push(TypePart::Separator("dyn_"));
475 }
476 fill_type_pieces_from_array_of_inputs(type_pieces, &trait_object.bounds, "+", |type_pieces, bound| match bound {
477 syn::TypeParamBound::Trait(trait_bound) => fill_type_pieces_from_type_path(type_pieces, &trait_bound.path),
478 syn::TypeParamBound::Lifetime(_) => Ok(()),
479 })
480 }
481 syn::Type::Never(_) => Err("syn::Type::Never is not implemented yet."),
482 syn::Type::Group(_) => Err("syn::Type::Group is not implemented yet."),
483 syn::Type::Infer(_) => Err("syn::Type::Infer is not implemented yet."),
484 syn::Type::Macro(_) => Err("syn::Type::Macro is not implemented yet."),
485 syn::Type::Verbatim(_) => Err("syn::Type::Verbatim is not implemented yet."),
486 }
487}
488
489fn fill_type_pieces_from_type_path<'a>(type_pieces: &mut Vec<TypePart<'a>>, path: &'a syn::Path) -> Result<(), &'static str> {
490 for segment in path.segments.iter() {
491 type_pieces.push(TypePart::Ident(&segment.ident));
492 fill_type_pieces_from_path_arguments(type_pieces, &segment.arguments)?;
493 }
494 Ok(())
495}
496
497fn fill_type_pieces_from_path_arguments<'a>(type_pieces: &mut Vec<TypePart<'a>>, arguments: &'a syn::PathArguments) -> Result<(), &'static str> {
498 match arguments {
499 syn::PathArguments::AngleBracketed(ref angle) => {
500 type_pieces.push(TypePart::Separator("<"));
501 fill_type_pieces_from_array_of_inputs(type_pieces, &angle.args, ",", |type_pieces, arg| match arg {
502 syn::GenericArgument::Type(ref ty) => fill_type_pieces_from_type(type_pieces, ty),
503 syn::GenericArgument::Lifetime(_) => Ok(()),
504 syn::GenericArgument::Binding(_) => Ok(()),
505 syn::GenericArgument::Constraint(_) => Ok(()),
506 syn::GenericArgument::Const(_) => Ok(()),
507 })?;
508 type_pieces.push(TypePart::Separator(">"));
509 }
510 syn::PathArguments::None => {}
511 syn::PathArguments::Parenthesized(ref paren) => {
512 type_pieces.push(TypePart::Separator("("));
513 fill_type_pieces_from_array_of_inputs(type_pieces, &paren.inputs, ",", fill_type_pieces_from_type)?;
514 type_pieces.push(TypePart::Separator(")"));
515 fill_type_pieces_from_return_type(type_pieces, &paren.output)?;
516 }
517 }
518 Ok(())
519}
520
521fn fill_type_pieces_from_return_type<'a>(type_pieces: &mut Vec<TypePart<'a>>, output: &'a syn::ReturnType) -> Result<(), &'static str> {
522 match output {
523 syn::ReturnType::Default => Ok(()),
524 syn::ReturnType::Type(_, ref arg) => fill_type_pieces_from_type(type_pieces, &**arg),
525 }
526}
527
528fn fill_type_pieces_from_array_of_inputs<'a, T, U>(
529 type_pieces: &mut Vec<TypePart<'a>>,
530 inputs: &'a syn::punctuated::Punctuated<T, U>,
531 separator: &'static str,
532 action: impl Fn(&mut Vec<TypePart<'a>>, &'a T) -> Result<(), &'static str>,
533) -> Result<(), &'static str> {
534 if !inputs.is_empty() {
535 for arg in inputs {
536 action(type_pieces, arg)?;
537 match type_pieces[type_pieces.len() - 1] {
538 TypePart::Separator(_s) if _s == separator => {}
539 _ => type_pieces.push(TypePart::Separator(separator)),
540 }
541 }
542 match type_pieces[type_pieces.len() - 1] {
543 TypePart::Separator(_s) if _s == separator => type_pieces.truncate(type_pieces.len() - 1),
544 _ => {}
545 }
546 }
547 Ok(())
548}
549
550fn make_type_name_from_type_pieces(type_pieces: Vec<TypePart>) -> String {
551 type_pieces
552 .into_iter()
553 .map(|piece| piece.to_string())
554 .collect::<String>()
555 .to_lowercase()
556 .chars()
557 .map(|c| match c {
558 '<' | '>' | '(' | ')' | '[' | ']' | '-' | ',' | ';' => '_',
559 _ => c,
560 })
561 .collect()
562}