getset/lib.rs
1/*!
2Getset, we're ready to go!
3
4A procedural macro for generating the most basic getters and setters on fields.
5
6Getters are generated as `fn field(&self) -> &type`, while setters are generated as `fn field(&mut self, val: type)`.
7
8These macros are not intended to be used on fields which require custom logic inside of their setters and getters. Just write your own in that case!
9
10```rust
11use getset::{CopyGetters, Getters, MutGetters, Setters};
12
13#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
14pub struct Foo<T>
15where
16 T: Copy + Clone + Default,
17{
18 /// Doc comments are supported!
19 /// Multiline, even.
20 #[getset(get, set, get_mut)]
21 private: T,
22
23 /// Doc comments are supported!
24 /// Multiline, even.
25 #[getset(get_copy = "pub", set = "pub", get_mut = "pub")]
26 public: T,
27}
28
29let mut foo = Foo::default();
30foo.set_private(1);
31(*foo.private_mut()) += 1;
32assert_eq!(*foo.private(), 2);
33```
34
35You can use `cargo-expand` to generate the output. Here are the functions that the above generates (Replicate with `cargo expand --example simple`):
36
37```rust,ignore
38use getset::{Getters, MutGetters, CopyGetters, Setters};
39pub struct Foo<T>
40where
41 T: Copy + Clone + Default,
42{
43 /// Doc comments are supported!
44 /// Multiline, even.
45 #[getset(get, get, get_mut)]
46 private: T,
47 /// Doc comments are supported!
48 /// Multiline, even.
49 #[getset(get_copy = "pub", set = "pub", get_mut = "pub")]
50 public: T,
51}
52impl<T> Foo<T>
53where
54 T: Copy + Clone + Default,
55{
56 /// Doc comments are supported!
57 /// Multiline, even.
58 #[inline(always)]
59 fn private(&self) -> &T {
60 &self.private
61 }
62}
63impl<T> Foo<T>
64where
65 T: Copy + Clone + Default,
66{
67 /// Doc comments are supported!
68 /// Multiline, even.
69 #[inline(always)]
70 pub fn set_public(&mut self, val: T) -> &mut Self {
71 self.public = val;
72 self
73 }
74}
75impl<T> Foo<T>
76where
77 T: Copy + Clone + Default,
78{
79 /// Doc comments are supported!
80 /// Multiline, even.
81 #[inline(always)]
82 fn private_mut(&mut self) -> &mut T {
83 &mut self.private
84 }
85 /// Doc comments are supported!
86 /// Multiline, even.
87 #[inline(always)]
88 pub fn public_mut(&mut self) -> &mut T {
89 &mut self.public
90 }
91}
92impl<T> Foo<T>
93where
94 T: Copy + Clone + Default,
95{
96 /// Doc comments are supported!
97 /// Multiline, even.
98 #[inline(always)]
99 pub fn public(&self) -> T {
100 self.public
101 }
102}
103```
104
105Attributes can be set on struct level for all fields in struct as well. Field level attributes take
106precedence.
107
108```rust
109mod submodule {
110 use getset::{Getters, MutGetters, CopyGetters, Setters};
111 #[derive(Getters, CopyGetters, Default)]
112 #[getset(get_copy = "pub")] // By default add a pub getting for all fields.
113 pub struct Foo {
114 public: i32,
115 #[getset(get_copy)] // Override as private
116 private: i32,
117 }
118 fn demo() {
119 let mut foo = Foo::default();
120 foo.private();
121 }
122}
123
124let mut foo = submodule::Foo::default();
125foo.public();
126```
127
128For some purposes, it's useful to have the `get_` prefix on the getters for
129either legacy of compatibility reasons. It is done with `with_prefix`.
130
131```rust
132use getset::{Getters, MutGetters, CopyGetters, Setters};
133
134#[derive(Getters, Default)]
135pub struct Foo {
136 #[getset(get = "pub with_prefix")]
137 field: bool,
138}
139
140
141let mut foo = Foo::default();
142let val = foo.get_field();
143```
144
145Skipping setters and getters generation for a field when struct level attribute is used
146is possible with `#[getset(skip)]`.
147
148```rust
149use getset::{CopyGetters, Setters};
150
151#[derive(CopyGetters, Setters)]
152#[getset(get_copy, set)]
153pub struct Foo {
154 // If the field was not skipped, the compiler would complain about moving
155 // a non-copyable type in copy getter.
156 #[getset(skip)]
157 skipped: String,
158
159 field1: usize,
160 field2: usize,
161}
162
163impl Foo {
164 // It is possible to write getters and setters manually,
165 // possibly with a custom logic.
166 fn skipped(&self) -> &str {
167 &self.skipped
168 }
169
170 fn set_skipped(&mut self, val: &str) -> &mut Self {
171 self.skipped = val.to_string();
172 self
173 }
174}
175```
176*/
177
178#[macro_use]
179extern crate quote;
180
181use proc_macro::TokenStream;
182use proc_macro2::TokenStream as TokenStream2;
183use proc_macro_error2::{abort, abort_call_site, proc_macro_error};
184use syn::{parse_macro_input, spanned::Spanned, DataStruct, DeriveInput, Meta};
185
186use crate::generate::{GenMode, GenParams};
187
188mod generate;
189
190#[proc_macro_derive(Getters, attributes(get, with_prefix, getset))]
191#[proc_macro_error]
192pub fn getters(input: TokenStream) -> TokenStream {
193 let ast = parse_macro_input!(input as DeriveInput);
194 let params = GenParams {
195 mode: GenMode::Get,
196 global_attr: parse_global_attr(&ast.attrs, GenMode::Get),
197 };
198
199 produce(&ast, ¶ms).into()
200}
201
202#[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix, getset))]
203#[proc_macro_error]
204pub fn copy_getters(input: TokenStream) -> TokenStream {
205 let ast = parse_macro_input!(input as DeriveInput);
206 let params = GenParams {
207 mode: GenMode::GetCopy,
208 global_attr: parse_global_attr(&ast.attrs, GenMode::GetCopy),
209 };
210
211 produce(&ast, ¶ms).into()
212}
213
214#[proc_macro_derive(MutGetters, attributes(get_mut, getset))]
215#[proc_macro_error]
216pub fn mut_getters(input: TokenStream) -> TokenStream {
217 let ast = parse_macro_input!(input as DeriveInput);
218 let params = GenParams {
219 mode: GenMode::GetMut,
220 global_attr: parse_global_attr(&ast.attrs, GenMode::GetMut),
221 };
222
223 produce(&ast, ¶ms).into()
224}
225
226#[proc_macro_derive(Setters, attributes(set, getset))]
227#[proc_macro_error]
228pub fn setters(input: TokenStream) -> TokenStream {
229 let ast = parse_macro_input!(input as DeriveInput);
230 let params = GenParams {
231 mode: GenMode::Set,
232 global_attr: parse_global_attr(&ast.attrs, GenMode::Set),
233 };
234
235 produce(&ast, ¶ms).into()
236}
237
238fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option<Meta> {
239 attrs.iter().filter_map(|v| parse_attr(v, mode)).last()
240}
241
242fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option<syn::Meta> {
243 use syn::{punctuated::Punctuated, Token};
244
245 if attr.path().is_ident("getset") {
246 let meta_list =
247 match attr.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
248 Ok(list) => list,
249 Err(e) => abort!(attr.span(), "Failed to parse getset attribute: {}", e),
250 };
251
252 let (last, skip, mut collected) = meta_list
253 .into_iter()
254 .inspect(|meta| {
255 if !(meta.path().is_ident("get")
256 || meta.path().is_ident("get_copy")
257 || meta.path().is_ident("get_mut")
258 || meta.path().is_ident("set")
259 || meta.path().is_ident("skip"))
260 {
261 abort!(meta.path().span(), "unknown setter or getter")
262 }
263 })
264 .fold(
265 (None, None, Vec::new()),
266 |(last, skip, mut collected), meta| {
267 if meta.path().is_ident(mode.name()) {
268 (Some(meta), skip, collected)
269 } else if meta.path().is_ident("skip") {
270 (last, Some(meta), collected)
271 } else {
272 collected.push(meta);
273 (last, skip, collected)
274 }
275 },
276 );
277
278 if skip.is_some() {
279 // Check if there is any setter or getter used with skip, which is
280 // forbidden.
281 if last.is_none() && collected.is_empty() {
282 skip
283 } else {
284 abort!(
285 last.or_else(|| collected.pop()).unwrap().path().span(),
286 "use of setters and getters with skip is invalid"
287 );
288 }
289 } else {
290 last
291 }
292 } else if attr.path().is_ident(mode.name()) {
293 // If skip is not used, return the last occurrence of matching
294 // setter/getter, if there is any.
295 attr.meta.clone().into()
296 } else {
297 None
298 }
299}
300
301fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
302 let name = &ast.ident;
303 let generics = &ast.generics;
304 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
305
306 // Is it a struct?
307 if let syn::Data::Struct(DataStruct { ref fields, .. }) = ast.data {
308 let generated = fields.iter().map(|f| generate::implement(f, params));
309
310 quote! {
311 impl #impl_generics #name #ty_generics #where_clause {
312 #(#generated)*
313 }
314 }
315 } else {
316 // Nope. This is an Enum. We cannot handle these!
317 abort_call_site!("#[derive(Getters)] is only defined for structs, not for enums!");
318 }
319}