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, WithSetters};
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, WithSetters};
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, WithSetters};
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, WithSetters};
150
151#[derive(CopyGetters, Setters, WithSetters)]
152#[getset(get_copy, set, set_with)]
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 fn with_skipped(mut self, val: &str) -> Self {
176 self.skipped = val.to_string();
177 self
178 }
179}
180```
181
182For a unary struct (a tuple struct with a single field),
183the macro generates the `get`, `get_mut`, and `set` functions to
184provide a getter, a mutable getter, and a setter, respectively.
185
186```rust
187use getset::{Getters, MutGetters, CopyGetters, Setters};
188
189#[derive(Setters, Getters, MutGetters)]
190struct UnaryTuple(#[getset(set, get, get_mut)] i32);
191
192let mut tup = UnaryTuple(42);
193assert_eq!(tup.get(), &42);
194assert_eq!(tup.get_mut(), &mut 42);
195tup.set(43);
196assert_eq!(tup.get(), &43);
197
198#[derive(CopyGetters)]
199struct CopyUnaryTuple(#[getset(get_copy)] i32);
200
201let tup = CopyUnaryTuple(42);
202```
203*/
204
205#[macro_use]
206extern crate quote;
207
208use proc_macro::TokenStream;
209use proc_macro2::TokenStream as TokenStream2;
210use proc_macro_error2::{abort, abort_call_site, proc_macro_error};
211use syn::{parse_macro_input, spanned::Spanned, DataStruct, DeriveInput, Meta};
212
213use crate::generate::{GenMode, GenParams};
214
215mod generate;
216
217#[proc_macro_derive(Getters, attributes(get, with_prefix, getset))]
218#[proc_macro_error]
219pub fn getters(input: TokenStream) -> TokenStream {
220 let ast = parse_macro_input!(input as DeriveInput);
221 let params = GenParams {
222 mode: GenMode::Get,
223 global_attr: parse_global_attr(&ast.attrs, GenMode::Get),
224 };
225
226 produce(&ast, ¶ms).into()
227}
228
229#[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix, getset))]
230#[proc_macro_error]
231pub fn copy_getters(input: TokenStream) -> TokenStream {
232 let ast = parse_macro_input!(input as DeriveInput);
233 let params = GenParams {
234 mode: GenMode::GetCopy,
235 global_attr: parse_global_attr(&ast.attrs, GenMode::GetCopy),
236 };
237
238 produce(&ast, ¶ms).into()
239}
240
241#[proc_macro_derive(MutGetters, attributes(get_mut, getset))]
242#[proc_macro_error]
243pub fn mut_getters(input: TokenStream) -> TokenStream {
244 let ast = parse_macro_input!(input as DeriveInput);
245 let params = GenParams {
246 mode: GenMode::GetMut,
247 global_attr: parse_global_attr(&ast.attrs, GenMode::GetMut),
248 };
249
250 produce(&ast, ¶ms).into()
251}
252
253#[proc_macro_derive(Setters, attributes(set, getset))]
254#[proc_macro_error]
255pub fn setters(input: TokenStream) -> TokenStream {
256 let ast = parse_macro_input!(input as DeriveInput);
257 let params = GenParams {
258 mode: GenMode::Set,
259 global_attr: parse_global_attr(&ast.attrs, GenMode::Set),
260 };
261
262 produce(&ast, ¶ms).into()
263}
264
265#[proc_macro_derive(WithSetters, attributes(set_with, getset))]
266#[proc_macro_error]
267pub fn with_setters(input: TokenStream) -> TokenStream {
268 let ast = parse_macro_input!(input as DeriveInput);
269 let params = GenParams {
270 mode: GenMode::SetWith,
271 global_attr: parse_global_attr(&ast.attrs, GenMode::SetWith),
272 };
273
274 produce(&ast, ¶ms).into()
275}
276
277fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option<Meta> {
278 attrs.iter().filter_map(|v| parse_attr(v, mode)).last()
279}
280
281fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option<syn::Meta> {
282 use syn::{punctuated::Punctuated, Token};
283
284 if attr.path().is_ident("getset") {
285 let meta_list =
286 match attr.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
287 Ok(list) => list,
288 Err(e) => abort!(attr.span(), "Failed to parse getset attribute: {}", e),
289 };
290
291 let (last, skip, mut collected) = meta_list
292 .into_iter()
293 .inspect(|meta| {
294 if !(meta.path().is_ident("get")
295 || meta.path().is_ident("get_copy")
296 || meta.path().is_ident("get_mut")
297 || meta.path().is_ident("set")
298 || meta.path().is_ident("set_with")
299 || meta.path().is_ident("skip"))
300 {
301 abort!(meta.path().span(), "unknown setter or getter")
302 }
303 })
304 .fold(
305 (None, None, Vec::new()),
306 |(last, skip, mut collected), meta| {
307 if meta.path().is_ident(mode.name()) {
308 (Some(meta), skip, collected)
309 } else if meta.path().is_ident("skip") {
310 (last, Some(meta), collected)
311 } else {
312 collected.push(meta);
313 (last, skip, collected)
314 }
315 },
316 );
317
318 if skip.is_some() {
319 // Check if there is any setter or getter used with skip, which is
320 // forbidden.
321 if last.is_none() && collected.is_empty() {
322 skip
323 } else {
324 abort!(
325 last.or_else(|| collected.pop()).unwrap().path().span(),
326 "use of setters and getters with skip is invalid"
327 );
328 }
329 } else {
330 last
331 }
332 } else if attr.path().is_ident(mode.name()) {
333 // If skip is not used, return the last occurrence of matching
334 // setter/getter, if there is any.
335 attr.meta.clone().into()
336 } else {
337 None
338 }
339}
340
341fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
342 let name = &ast.ident;
343 let generics = &ast.generics;
344 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
345
346 // Is it a struct?
347 if let syn::Data::Struct(DataStruct { ref fields, .. }) = ast.data {
348 // Handle unary struct
349 if matches!(fields, syn::Fields::Unnamed(_)) {
350 if fields.len() != 1 {
351 abort_call_site!("Only support unary struct!");
352 }
353 // This unwrap is safe because we know there is exactly one field
354 let field = fields.iter().next().unwrap();
355 let generated = generate::implement_for_unnamed(field, params);
356
357 quote! {
358 impl #impl_generics #name #ty_generics #where_clause {
359 #generated
360 }
361 }
362 } else {
363 let generated = fields.iter().map(|f| generate::implement(f, params));
364
365 quote! {
366 impl #impl_generics #name #ty_generics #where_clause {
367 #(#generated)*
368 }
369 }
370 }
371 } else {
372 // Nope. This is an Enum. We cannot handle these!
373 abort_call_site!("#[derive(Getters)] is only defined for structs, not for enums!");
374 }
375}