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#[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(¤t))
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}