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, &params);
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, &params);
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, &params);
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, &params);
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}