getset_scoped/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_scoped::{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_scoped::{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_scoped::{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
128Skipping setters and getters generation for a field when struct level attribute is used
129is possible with `#[getset(skip)]`.
130
131```rust
132use getset_scoped::{CopyGetters, Setters};
133
134#[derive(CopyGetters, Setters)]
135#[getset(get_copy, set)]
136pub struct Foo {
137 // If the field was not skipped, the compiler would complain about moving
138 // a non-copyable type in copy getter.
139 #[getset(skip)]
140 skipped: String,
141
142 field1: usize,
143 field2: usize,
144}
145
146impl Foo {
147 // It is possible to write getters and setters manually,
148 // possibly with a custom logic.
149 fn skipped(&self) -> &str {
150 &self.skipped
151 }
152
153 fn set_skipped(&mut self, val: &str) -> &mut Self {
154 self.skipped = val.to_string();
155 self
156 }
157}
158```
159*/
160
161extern crate proc_macro;
162extern crate syn;
163#[macro_use]
164extern crate quote;
165extern crate proc_macro2;
166
167use proc_macro::TokenStream;
168use proc_macro2::TokenStream as TokenStream2;
169use proc_macro_error::{abort, abort_call_site, proc_macro_error, ResultExt};
170use syn::{spanned::Spanned, DataStruct, DeriveInput, Meta};
171
172mod generate;
173use crate::generate::{GenMode, GenParams};
174
175#[proc_macro_derive(Getters, attributes(get, with_prefix, getset))]
176#[proc_macro_error]
177pub fn getters(input: TokenStream) -> TokenStream {
178 // Parse the string representation
179 let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters");
180 let params = GenParams {
181 mode: GenMode::Get,
182 global_attr: parse_global_attr(&ast.attrs, GenMode::Get),
183 };
184
185 // Build the impl
186 let gen = produce(&ast, ¶ms);
187
188 // Return the generated impl
189 gen.into()
190}
191
192#[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix, getset))]
193#[proc_macro_error]
194pub fn copy_getters(input: TokenStream) -> TokenStream {
195 // Parse the string representation
196 let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters");
197 let params = GenParams {
198 mode: GenMode::GetCopy,
199 global_attr: parse_global_attr(&ast.attrs, GenMode::GetCopy),
200 };
201
202 // Build the impl
203 let gen = produce(&ast, ¶ms);
204
205 // Return the generated impl
206 gen.into()
207}
208
209#[proc_macro_derive(MutGetters, attributes(get_mut, getset))]
210#[proc_macro_error]
211pub fn mut_getters(input: TokenStream) -> TokenStream {
212 // Parse the string representation
213 let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters");
214 let params = GenParams {
215 mode: GenMode::GetMut,
216 global_attr: parse_global_attr(&ast.attrs, GenMode::GetMut),
217 };
218
219 // Build the impl
220 let gen = produce(&ast, ¶ms);
221 // Return the generated impl
222 gen.into()
223}
224
225#[proc_macro_derive(Setters, attributes(set, getset))]
226#[proc_macro_error]
227pub fn setters(input: TokenStream) -> TokenStream {
228 // Parse the string representation
229 let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for setters");
230 let params = GenParams {
231 mode: GenMode::Set,
232 global_attr: parse_global_attr(&ast.attrs, GenMode::Set),
233 };
234
235 // Build the impl
236 let gen = produce(&ast, ¶ms);
237
238 // Return the generated impl
239 gen.into()
240}
241
242fn parse_global_attr(attrs: &[syn::Attribute], mode: GenMode) -> Option<Meta> {
243 attrs
244 .iter()
245 .filter_map(|v| parse_attr(v, mode)) // non "meta" attributes are not our concern
246 .last()
247}
248
249fn parse_attr(attr: &syn::Attribute, mode: GenMode) -> Option<Meta> {
250 use syn::{punctuated::Punctuated, Token};
251
252 if attr.path.is_ident("getset") {
253 let (last, skip, mut collected) = attr
254 .parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
255 .unwrap_or_abort()
256 .into_iter()
257 .inspect(|meta| {
258 if !(meta.path().is_ident("get")
259 || meta.path().is_ident("get_copy")
260 || meta.path().is_ident("get_mut")
261 || meta.path().is_ident("set")
262 || meta.path().is_ident("skip"))
263 {
264 abort!(meta.path().span(), "unknown setter or getter")
265 }
266 })
267 .fold(
268 (None, None, Vec::new()),
269 |(last, skip, mut collected), meta| {
270 if meta.path().is_ident(mode.name()) {
271 (Some(meta), skip, collected)
272 } else if meta.path().is_ident("skip") {
273 (last, Some(meta), collected)
274 } else {
275 // Store inapplicable item for potential error message
276 // if used with skip.
277 collected.push(meta);
278 (last, skip, collected)
279 }
280 },
281 );
282
283 if skip.is_some() {
284 // Check if there is any setter or getter used with skip, which is
285 // forbidden.
286 if last.is_none() && collected.is_empty() {
287 skip
288 } else {
289 abort!(
290 last.or_else(|| collected.pop()).unwrap().path().span(),
291 "use of setters and getters with skip is invalid"
292 );
293 }
294 } else {
295 // If skip is not used, return the last occurrence of matching
296 // setter/getter, if there is any.
297 last
298 }
299 } else {
300 attr.parse_meta()
301 .ok()
302 .filter(|meta| meta.path().is_ident(mode.name()))
303 }
304}
305
306fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
307 let name = &ast.ident;
308 let generics = &ast.generics;
309 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
310
311 // Is it a struct?
312 if let syn::Data::Struct(DataStruct { ref fields, .. }) = ast.data {
313 let generated = fields.iter().map(|f| generate::implement(f, params));
314
315 quote! {
316 impl #impl_generics #name #ty_generics #where_clause {
317 #(#generated)*
318 }
319 }
320 } else {
321 // Nope. This is an Enum. We cannot handle these!
322 abort_call_site!("#[derive(Getters)] is only defined for structs, not for enums!");
323 }
324}