accessors_rs/
lib.rs

1#[cfg(test)]
2mod tests;
3
4mod accessors_attr;
5mod error;
6
7use proc_macro2::{Ident, TokenStream};
8use quote::{format_ident, quote, ToTokens};
9use syn::{Attribute, Data, DeriveInput, Fields, Generics, Meta, Type};
10
11use crate::accessors_attr::AccessorsAttrData;
12
13/// Derive macro generating an impl for accessing the fields of a struct.\
14/// Use `#[accessors(get, get_mut, set)]` to defined with accessors you want to have on a field.\
15/// 
16/// List of `accessors` param.
17/// - `get`: Generate a getter returning a reference.\
18/// - `get_copy`: Generate a getter returning a copy. (mutually exclusive with get)\
19/// - `get_mut`: Generate a mutable getter returning a mutable reference.\
20/// - `set`: Generate a setter.
21/// 
22/// Using `#[accessors(...)]` on a *field* will generate accessors for this specific field.\
23/// Using `#[accessors(...)]` on a *struct* will generate accessors for all field in the struct.
24///
25#[proc_macro_derive(Accessors, attributes(accessors))]
26pub fn accessors_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
27    accessors_derive_inner(input.into())
28        .unwrap_or_else(|e| e.into_compile_error())
29        .into()
30}
31
32fn accessors_derive_inner(input: TokenStream) -> syn::Result<TokenStream> {
33    let DeriveInput {
34        data,
35        generics,
36        ident,
37        attrs,
38        ..
39    } = syn::parse2(input)?;
40
41    let default_accessors_attr_data = parse_meta_vec_for_accessors_attrs(attrs)?;
42    let default_accessors_attr_data = reduce_meta_iter_to_accessors_attr_data(default_accessors_attr_data)?;
43
44    let mut accessors_fn_token_stream = TokenStream::new();
45    for field in get_struct_fields(data)? {
46        let accessors_attr_data = parse_meta_vec_for_accessors_attrs(field.attrs)?;
47        let accessors_attr_data = reduce_meta_iter_to_accessors_attr_data(accessors_attr_data)?;
48
49        if let Some(field_ident) = field.ident {
50            get_accessors_func_for_field_token_stream(
51                field_ident,
52                field.ty,
53                default_accessors_attr_data.merge(&accessors_attr_data),
54            )
55            .to_tokens(&mut accessors_fn_token_stream);
56        }
57    }
58
59    Ok(get_impl_token_stream(
60        ident,
61        generics,
62        accessors_fn_token_stream,
63    ))
64}
65
66fn parse_meta_vec_for_accessors_attrs(
67    struct_attrs: Vec<Attribute>,
68) -> syn::Result<impl Iterator<Item = Meta>> {
69    error::combine_syn_errors(
70        struct_attrs
71            .into_iter()
72            .filter(|a| accessors_attr::is_accessors_path(&a.path))
73            .map(|a| a.parse_meta()),
74    )
75}
76
77fn reduce_meta_iter_to_accessors_attr_data(
78    meta_iter: impl Iterator<Item = Meta>,
79) -> syn::Result<AccessorsAttrData> {
80    Ok(
81        error::combine_syn_errors(meta_iter.map(AccessorsAttrData::try_from))?
82            .reduce(|accessors_attr_data, current| accessors_attr_data.merge(&current))
83            .unwrap_or_default(),
84    )
85}
86
87fn get_struct_fields(data: Data) -> syn::Result<Fields> {
88    match data {
89        Data::Struct(struct_data) => Ok(struct_data.fields),
90        Data::Enum(data_enum) => Err(syn::Error::new_spanned(
91            data_enum.enum_token,
92            error::ACCESSORS_ON_ENUM_ERROR_MESSAGE,
93        )),
94        Data::Union(data_union) => Err(syn::Error::new_spanned(
95            data_union.union_token,
96            error::ACCESSORS_ON_UNION_ERROR_MESSAGE,
97        )),
98    }
99}
100
101fn get_accessors_func_for_field_token_stream(
102    field_ident: Ident,
103    field_type: Type,
104    accessors_attr_data: AccessorsAttrData,
105) -> TokenStream {
106    let get_fn = accessors_attr_data.get.map(|kind| {
107        let ref_token = match kind {
108            accessors_attr::GetKind::Ref => Some(quote! { & }),
109            accessors_attr::GetKind::Copy => None,
110        };
111        quote! {
112            pub fn #field_ident(&self) -> #ref_token #field_type {
113                #ref_token self.#field_ident
114            }
115        }
116    });
117
118    let get_mut_fn = accessors_attr_data.get_mut.map(|_| {
119        let field_ident_mut = format_ident!("{field_ident}_mut");
120        quote! {
121            pub fn #field_ident_mut(&mut self) -> &mut #field_type {
122                &mut self.#field_ident
123            }
124        }
125    });
126
127    let set_fn = accessors_attr_data.set.map(|_| {
128        let set_field_ident = format_ident!("set_{field_ident}");
129        quote! {
130            pub fn #set_field_ident(&mut self, #field_ident: #field_type) {
131                self.#field_ident = #field_ident;
132            }
133        }
134    });
135
136    quote! {
137        #get_fn
138        #get_mut_fn
139        #set_fn
140    }
141}
142
143fn get_impl_token_stream(
144    struct_ident: Ident,
145    generics: Generics,
146    accessors_fn_token_stream: TokenStream,
147) -> TokenStream {
148    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
149    quote! {
150        impl #impl_generics #struct_ident #ty_generics #where_clause {
151            #accessors_fn_token_stream
152        }
153    }
154}