nebula_derive/
lib.rs

1//! # Getter Setter Macro
2//!
3//!
4//! ```
5//! ```
6
7use darling::{FromDeriveInput, FromField};
8use proc_macro::TokenStream;
9use proc_macro::{self, LexError};
10use quote::quote;
11use std::str::FromStr;
12use syn::punctuated::Punctuated;
13use syn::token::Comma;
14use syn::{
15    parse_macro_input, DataEnum, DataUnion, DeriveInput, Field, FieldsNamed, FieldsUnnamed, Ident,
16    Type,
17};
18
19macro_rules! getter {
20    ($idn:expr, $typ:expr) => {
21        format!(
22            r##"pub fn {idn}(&self) -> &{ty} {{&self.{idn}}}"##,
23            idn = $idn,
24            ty = $typ
25        )
26    };
27}
28
29macro_rules! java_getter {
30    ($func:expr,$idn:expr, $typ:expr) => {
31        format!(
32            r##"pub fn get_{idn}(&self) -> &{ty} {{&self.{idn}}}"##,
33            idn = $idn,
34            ty = $typ
35        )
36    };
37}
38
39macro_rules! setter {
40    ($func:expr, $idn:expr, $typ:expr) => {
41        format!(
42            r##"pub fn {idn}_mut(&mut self) -> &mut {ty} {{&mut self.{idn}}}"##,
43            idn = $idn,
44            ty = $typ
45        )
46    };
47}
48
49macro_rules! java_setter {
50    ($idn:expr, $typ:expr) => {
51    format!(r##"pub fn set_{idn}(&mut self, value: {ty}) -> &mut Self {{self.{idn} = value; self}}"##,
52        idn = $idn,
53        ty = $typ
54    )
55};
56}
57
58enum SetterGetter {
59    Get {
60        func_name: String,
61        field_type: String,
62        field_name: String,
63    },
64    JavaGet {
65        func_name: String,
66        field_type: String,
67        field_name: String,
68    },
69    Set {
70        func_name: String,
71        field_type: String,
72        field_name: String,
73    },
74    JavaSet {
75        func_name: String,
76        field_type: String,
77        field_name: String,
78    },
79}
80
81impl From<SetterGetter> for String {
82    fn from(setget: SetterGetter) -> Self {
83        match setget {
84            SetterGetter::Get {
85                func_name,
86                field_type,
87                field_name,
88            } => format! (r##"
89
90pub fn {func_name}(&self) -> &{field_type} {{
91&self.{field_name}
92}}
93"##, func_name = func_name, field_type = field_type, field_name= field_name),
94            SetterGetter::JavaGet {
95                func_name,
96                field_type,
97                field_name,
98            } => format! ("pub fn {func_name}(&self) -> &{field_type} {{&self.{field_name}}}", func_name = func_name, field_type = field_type, field_name= field_name),
99            SetterGetter::Set {
100                func_name,
101                field_type,
102                field_name,
103            } => format! ("pub fn {func_name}(&mut self) -> &mut {field_type} {{&mut self.{field_name}}}", func_name = func_name, field_type = field_type, field_name= field_name),
104            SetterGetter::JavaSet {
105                func_name,
106                field_type,
107                field_name,
108            } => format!("pub fn {func_name}(&mut self, value: {field_type}) -> &mut Self {{self.{field_name} = value; self}}", func_name = func_name, field_type = field_type, field_name= field_name),
109        }
110    }
111}
112
113#[derive(FromDeriveInput, Default)]
114#[darling(default, attributes(simplify), forward_attrs(allow, doc, cfg))]
115struct Opts {
116    java_style: bool,
117    setter: bool,
118    getter: bool,
119}
120
121#[derive(FromField, Default)]
122#[darling(default, forward_attrs(allow, doc, cfg))]
123struct FieldOpts {
124    java_style: bool,
125    disabled: bool,
126    rename: Option<String>,
127}
128
129#[proc_macro_derive(Simplify, attributes(simplify))]
130pub fn simplify(input: TokenStream) -> TokenStream {
131    let input: DeriveInput = parse_macro_input!(input);
132    let opts = Opts::from_derive_input(&input).expect("Failed to parse GetterOpts");
133
134    let description = match input.data {
135        syn::Data::Struct(s) => match s.fields {
136            syn::Fields::Named(FieldsNamed { named, .. }) => handle_attributes(&opts, named),
137            syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
138                let num_fields = unnamed.iter().count();
139                format!("a struct with {} unnamed fields", num_fields);
140                Vec::new()
141            }
142            syn::Fields::Unit => {
143                format!("a unit struct");
144                Vec::new()
145            }
146        },
147        _ => panic!("Failure, this derive only works on Stucts"),
148    };
149
150    TokenStream::from_str(&format!(
151        r##"
152    impl {ident} {{
153        {functions}
154    }}"##,
155        ident = input.ident,
156        functions = description.join("\n")
157    ))
158    .unwrap()
159}
160
161fn handle_attributes(global_opts: &Opts, named: Punctuated<Field, Comma>) -> Vec<String> {
162    let idents = named
163        .iter()
164        .flat_map(|f| {
165            let field_opts = FieldOpts::from_field(f).expect("Failed to parse field options");
166            let mut res = Vec::new();
167            if let Some(s) = setter(global_opts, &field_opts, f) {
168                res.push(s)
169            }
170            if let Some(s) = getter(global_opts, &field_opts, f) {
171                res.push(s)
172            }
173            res
174        })
175        .map(|t| t.to_string())
176        .collect::<Vec<String>>();
177    idents
178}
179
180fn setter(global_opts: &Opts, opts: &FieldOpts, f: &Field) -> Option<String> {
181    let ident = &f.ident;
182    let field_type = &f.ty;
183    if global_opts.setter && !opts.disabled {
184        if global_opts.java_style {
185            let str_ident = ident.clone().map(|t| t.to_string());
186            if let (Some(func_name), Some(field_name)) = (
187                opts.rename
188                    .clone()
189                    .or_else(|| str_ident.clone().map(|str| format!("set_{}", str))),
190                str_ident,
191            ) {
192                Some(
193                    SetterGetter::JavaSet {
194                        func_name,
195                        field_type: (quote! {#field_type}).to_string(),
196                        field_name,
197                    }
198                    .into(),
199                )
200            } else {
201                None
202            }
203        } else {
204            let str_ident = ident.clone().map(|t| t.to_string());
205            if let (Some(func_name), Some(field_name)) = (
206                opts.rename
207                    .clone()
208                    .or_else(|| str_ident.clone().map(|str| format!("{}_mut", str))),
209                str_ident,
210            ) {
211                Some(
212                    SetterGetter::Set {
213                        func_name,
214                        field_type: (quote! {#field_type}).to_string(),
215                        field_name,
216                    }
217                    .into(),
218                )
219            } else {
220                None
221            }
222        }
223    } else {
224        None
225    }
226}
227
228fn getter(global_opts: &Opts, opts: &FieldOpts, f: &Field) -> Option<String> {
229    let ident = &f.ident;
230    let field_type = &f.ty;
231    if global_opts.setter && !opts.disabled {
232        if global_opts.java_style {
233            let str_ident = ident.clone().map(|t| t.to_string());
234            if let (Some(func_name), Some(field_name)) = (
235                opts.rename
236                    .clone()
237                    .or_else(|| str_ident.clone().map(|str| format!("get_{}", str))),
238                str_ident,
239            ) {
240                Some(
241                    SetterGetter::JavaGet {
242                        func_name,
243                        field_type: (quote! {#field_type}).to_string(),
244                        field_name,
245                    }
246                    .into(),
247                )
248            } else {
249                None
250            }
251        } else {
252            let str_ident = ident.clone().map(|t| t.to_string());
253            if let (Some(func_name), Some(field_name)) =
254                (opts.rename.clone().or_else(|| str_ident.clone()), str_ident)
255            {
256                Some(
257                    SetterGetter::Get {
258                        func_name,
259                        field_type: (quote! {#field_type}).to_string(),
260                        field_name,
261                    }
262                    .into(),
263                )
264            } else {
265                None
266            }
267        }
268    } else {
269        None
270    }
271}