1extern crate proc_macro;
2extern crate proc_macro2;
3
4use darling::{util::Flag, FromDeriveInput, FromField, FromMeta, ToTokens};
5use proc_macro::TokenStream;
6
7use quote::{format_ident, quote};
8use syn::{parse_macro_input, DeriveInput, GenericArgument, Path, PathArguments, Type};
9
10#[derive(Debug, Clone, FromMeta)]
11struct Retype {
12 to: String,
13 #[darling(rename = "re")]
14 restore: String,
15}
16
17impl Retype {
18 fn new(to: &str, restore: &str) -> Self {
19 Self {
20 to: to.to_string(),
21 restore: restore.to_string(),
22 }
23 }
24}
25
26#[derive(Debug, Clone, FromMeta)]
27struct GlobRetype {
28 from: String,
29 to: String,
30 #[darling(rename = "re")]
31 restore: String,
32}
33
34#[derive(FromField, Clone, Debug)]
35#[darling(attributes(engineer), forward_attrs(allow, doc, cfg))]
36struct EngineerField {
37 ident: Option<syn::Ident>,
38 ty: syn::Type,
39
40 default_value: Option<String>,
41 retype: Option<Retype>,
42
43 default: Flag,
44 str_retype: Flag,
46}
47
48impl EngineerField {
49 fn apply_shorthands(&mut self) {
50 if self.str_retype.is_present() {
51 self.retype = Some(Retype::new("impl Into<String>", ".into()"))
52 }
53
54 if self.default.is_present() {
55 self.default_value = Some("Default::default()".to_string())
59 }
61
62 if self.default_value.is_some() {
63 self.default = Flag::present()
64 }
65 }
66
67 fn is_option(&self) -> bool {
68 type_is_option(&self.ty) || self.default.is_present()
69 }
70
71 fn is_retyped(&self) -> bool {
72 self.retype.is_some()
73 }
74
75 fn retyped(&self) -> proc_macro2::TokenStream {
76 match &self.retype {
77 Some(retype) => retype.to.parse().unwrap(),
78 None => {
79 let ty = if type_is_option(&self.ty) {
80 extract_type_from_option(&self.ty).unwrap()
81 } else {
82 &self.ty
83 };
84
85 quote!(#ty)
86 }
87 }
88 }
89
90 fn restorer(&self) -> proc_macro2::TokenStream {
91 match &self.retype {
92 Some(retype) => retype.restore.parse().unwrap(),
93 None => Default::default(),
94 }
95 }
96
97 fn as_struct_field(&self) -> proc_macro2::TokenStream {
98 let name = &self.ident;
99 let ty = &self.ty;
100
101 quote!(#name: #ty,)
102 }
103
104 fn as_struct_setter(&self) -> proc_macro2::TokenStream {
105 let name = &self.ident;
106 let mut name_ts = quote!(#name);
107
108 if self.is_retyped() {
109 let restore = self.restorer();
110 quote!( : #name #restore).to_tokens(&mut name_ts)
111 }
112
113 name_ts
114 }
115
116 fn as_func_argument(&self) -> proc_macro2::TokenStream {
117 let name = &self.ident;
118 let ty = self.retyped();
119
120 quote!(#name: #ty,)
121 }
122}
123
124#[derive(FromDeriveInput, Clone, Debug)]
125#[darling(
126 attributes(engineer),
127 supports(struct_named),
128 forward_attrs(allow, doc, cfg)
129)]
130struct EngineerOptions {
131 ident: syn::Ident,
132 vis: syn::Visibility,
133 data: darling::ast::Data<darling::util::Ignored, EngineerField>,
134
135 #[darling(rename = "engineer_name")]
136 engineer_name_arg: Option<String>,
137 #[darling(rename = "builder_func")]
138 builder_func_arg: Option<String>,
139
140 #[darling(multiple, rename = "retype")]
141 retypes: Vec<GlobRetype>,
142 str_retype: Flag,
143 new: Flag,
144
145 #[darling(skip)]
146 fields_ref: Option<Vec<EngineerField>>,
147 #[darling(skip)]
148 engineer_name: Option<syn::Ident>,
149 #[darling(skip)]
150 builder_func: Option<syn::Ident>,
151}
152
153impl EngineerOptions {
154 fn from_derive_input_delegate(input: &DeriveInput) -> Result<EngineerOptions, darling::Error> {
155 let mut s = Self::from_derive_input(input)?
156 .apply_self_shorthands()
157 .apply_global_retypes()
158 .apply_fields_shorthands()
159 .set_custom_fields();
160
161 s.set_fields_ref();
162
163 Ok(s)
164 }
165
166 fn apply_self_shorthands(mut self) -> Self {
167 if self.str_retype.is_present() {
168 self.retypes.push(GlobRetype {
169 from: "String".to_string(),
170 to: "impl Into<String>".to_string(),
171 restore: ".into()".to_string(),
172 })
173 }
174
175 if self.new.is_present() {
176 self.builder_func_arg = Some("new".to_string());
177 }
178
179 self
180 }
181
182 fn apply_global_retypes(mut self) -> Self {
183 self.data = self.data.map_struct_fields(|mut f| {
184 let ty_str = if type_is_option(&f.ty) {
185 extract_type_from_option(&f.ty).unwrap()
186 } else {
187 &f.ty
188 }
189 .to_token_stream()
190 .to_string();
191
192 for r in &self.retypes {
193 if ty_str == r.from {
194 f.retype = Some(Retype {
195 to: r.to.clone(),
196 restore: r.restore.clone(),
197 });
198 }
199 }
200
201 f
202 });
203
204 self
205 }
206
207 fn apply_fields_shorthands(mut self) -> Self {
208 self.data = self.data.map_struct_fields(|mut f| {
209 f.apply_shorthands();
210 f
211 });
212
213 self
214 }
215
216 fn set_custom_fields(mut self) -> Self {
217 self.engineer_name = format_ident!(
218 "{}",
219 self.engineer_name_arg
220 .clone()
221 .unwrap_or(format!("{}Engineer", self.ident))
222 )
223 .into();
224
225 self.builder_func = format_ident!(
226 "{}",
227 self.builder_func_arg
228 .clone()
229 .unwrap_or_else(|| "engineer".to_string())
230 )
231 .into();
232
233 self
234 }
235
236 fn set_fields_ref(&mut self) {
237 self.fields_ref = Some(self.data.clone().take_struct().unwrap().fields);
238 }
239
240 fn engineer_name(&self) -> &Option<proc_macro2::Ident> {
242 &self.engineer_name
243 }
244
245 fn builder_name(&self) -> &Option<proc_macro2::Ident> {
247 &self.builder_func
248 }
249
250 fn fields_ref(&self) -> &Vec<EngineerField> {
251 self.fields_ref.as_ref().unwrap()
252 }
253}
254
255trait FieldsHelpers<'e>: Iterator<Item = &'e EngineerField> + Sized {
256 fn filter_normals(self) -> Vec<&'e EngineerField> {
257 self.filter(|f| !f.is_option()).collect()
258 }
259
260 fn filter_options(self) -> Vec<&'e EngineerField> {
261 self.filter(|f| f.is_option()).collect()
262 }
263
264 fn map_names(self) -> Vec<&'e Option<syn::Ident>> {
265 self.map(|f| &f.ident).collect()
266 }
267
268 fn map_types(self) -> Vec<&'e Type> {
269 self.map(|f| &f.ty).collect()
270 }
271}
272
273impl<'e, T> FieldsHelpers<'e> for T where T: Iterator<Item = &'e EngineerField> {}
274
275struct EngineerStructDefinition<'e>(&'e EngineerOptions);
276
277impl<'e> EngineerStructDefinition<'e> {
278 fn name(&self) -> &Option<proc_macro2::Ident> {
279 self.0.engineer_name()
280 }
281
282 fn struct_definition(&self) -> proc_macro2::TokenStream {
283 let struct_name = &self.0.ident;
284 let vis = &self.0.vis;
285 let engineer_name = &self.0.engineer_name();
286
287 let fields = self.0.fields_ref();
288 let struct_fields = fields.iter().map(|f| f.as_struct_field());
289 let names = fields.iter().map(|f| &f.ident);
290
291 quote! {
292 #vis struct #engineer_name {
293 #(
294 #struct_fields
295 )*
296 }
297
298 impl Builder<#struct_name> for #engineer_name {
299 fn done(self) -> #struct_name {
300 #struct_name {
301 #(
302 #names: self.#names,
303 )*
304 }
305 }
306 }
307
308 impl From<#engineer_name> for #struct_name
309 {
310 fn from(value: #engineer_name) -> Self {
311 value.done()
312 }
313 }
314 }
315 }
316
317 fn new_func(&self) -> proc_macro2::TokenStream {
318 let engineer_name = self.name();
319 let vis = &self.0.vis;
320
321 let fields = self.0.fields_ref();
322 let nrm_fields = fields.iter().filter_normals();
323
324 let opt_names = fields.iter().filter(|f| f.is_option()).map(|f| &f.ident);
325 let opt_values = fields
326 .iter()
327 .filter(|f| f.is_option())
328 .map(|f| match &f.default_value {
329 Some(sec) => {
330 let t = sec.parse::<proc_macro2::TokenStream>().unwrap();
331
332 if type_is_option(&f.ty) {
333 quote!(Some(#t))
334 } else {
335 quote!(#t)
336 }
337 }
338 _ => {
339 if type_is_option(&f.ty) {
340 quote!(None)
341 } else {
342 quote!(Default::default())
343 }
344 }
345 });
346
347 let func_args = nrm_fields.iter().map(|f| f.as_func_argument());
348 let struct_setters = nrm_fields.iter().map(|f| f.as_struct_setter());
349
350 quote! {
351 #vis fn new(#(#func_args)*) -> Self {
352 #engineer_name {
353 #(
354 #struct_setters,
355 )*
356
357 #(
358 #opt_names: #opt_values,
359 )*
360 }
361 }
362 }
363 }
364
365 fn opt_setters(&self) -> proc_macro2::TokenStream {
366 let vis = &self.0.vis;
367
368 let fields = self.0.fields_ref();
369 let opt_fields = fields.iter().filter(|f| f.is_option());
370 let opt_names = opt_fields.clone().map(|f| &f.ident);
371
372 let opt_types = opt_fields.clone().map(|f| f.retyped());
373 let opt_restores = opt_fields.map(|f| f.restorer());
374
375 quote! {
376 #(
377 #vis fn #opt_names(mut self, #opt_names: #opt_types) -> Self {
378 self.#opt_names = (#opt_names #opt_restores).into();
379 self
380 }
381 )*
382 }
383 }
384
385 fn struct_impl(&self) -> proc_macro2::TokenStream {
386 let engineer_name = &self.name();
387 let new_func = self.new_func();
388 let opt_setters = self.opt_setters();
389
390 quote! {
391 impl #engineer_name {
392 #new_func
393 #opt_setters
394 }
395 }
396 }
397}
398
399impl<'e> quote::ToTokens for EngineerStructDefinition<'e> {
400 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
401 let struct_definition = self.struct_definition();
402 let struct_impl = self.struct_impl();
403
404 quote! {
405 #struct_definition
406 #struct_impl
407 }
408 .to_tokens(tokens)
409 }
410}
411
412struct StructImpl<'e>(&'e EngineerOptions);
413
414impl<'e> StructImpl<'e> {
415 fn builder_func(&self) -> proc_macro2::TokenStream {
416 let engineer_name = self.0.engineer_name();
417 let builder_name = self.0.builder_name();
418 let vis = &self.0.vis;
419
420 let fields = self.0.fields_ref();
421 let nrm_fields = fields.iter().filter(|f| !f.is_option());
422 let nrm_names = nrm_fields.clone().map(|f| &f.ident);
423
424 let func_args = nrm_fields.clone().map(|f| f.as_func_argument());
425
426 quote! {
427 #vis fn #builder_name(#(#func_args)*) -> #engineer_name {
428 <#engineer_name>::new(#(#nrm_names,)*)
429 }
430 }
431 }
432}
433
434impl<'e> quote::ToTokens for StructImpl<'e> {
435 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
436 let name = &self.0.ident;
437 let builder_func = self.builder_func();
438
439 quote! { impl #name { #builder_func } }.to_tokens(tokens)
440 }
441}
442
443struct TraitImpl<'e>(&'e EngineerOptions);
444
445impl<'e> quote::ToTokens for TraitImpl<'e> {
446 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
447 let name = &self.0.ident;
448 let engineer_name = &self.0.engineer_name();
449
450 let fields = self.0.fields_ref();
451 let nrm_fields = fields.iter().filter(|f| !f.is_option());
452
453 let nrm_count = nrm_fields.clone().count();
454 let opt_count = fields.len() - nrm_count;
455 let nrm_fields_types = nrm_fields.map(|f| &f.ty);
456
457 let members = (0..nrm_count).map(|f| {
458 format!("required.{}", f)
459 .parse::<proc_macro2::TokenStream>()
460 .unwrap()
461 });
462
463 quote! {
464 impl Engineer for #name {
465 const NORMAL_FIELDS: usize = #nrm_count;
466 const OPTIONAL_FIELDS: usize = #opt_count;
467
468 type Builder = #engineer_name;
469 type Params = (#(#nrm_fields_types,)*);
470
471 fn builder(required: Self::Params) -> Self::Builder {
472 #engineer_name::new(#(#members,)*)
473 }
474 }
475 }
476 .to_tokens(tokens);
477 }
478}
479
480#[proc_macro_derive(Engineer, attributes(engineer))]
481pub fn engineer(input: TokenStream) -> TokenStream {
482 let input = parse_macro_input!(input as DeriveInput);
484 let engineer_opts = EngineerOptions::from_derive_input_delegate(&input).unwrap();
485
486 let engineer_struct_definition = EngineerStructDefinition(&engineer_opts);
487 let struct_impl = StructImpl(&engineer_opts);
488 let trait_impl = TraitImpl(&engineer_opts);
489
490 let expanded = quote! {
492 #engineer_struct_definition
493 #struct_impl
494 #trait_impl
495 };
496
497 TokenStream::from(expanded)
499}
500
501fn type_is_option(ty: &Type) -> bool {
502 match ty {
503 Type::Path(path_type) => {
504 path_type.path.leading_colon.is_none()
505 && path_type.path.segments.len() == 1
506 && path_type.path.segments.iter().next().unwrap().ident == "Option"
507 }
508 _ => false,
509 }
510}
511
512fn path_is_option(path: &Path) -> bool {
513 path.leading_colon.is_none()
514 && path.segments.len() == 1
515 && path.segments.iter().next().unwrap().ident == "Option"
516}
517
518fn extract_type_from_option(ty: &Type) -> Option<&Type> {
519 match ty {
520 Type::Path(typepath) if typepath.qself.is_none() && path_is_option(&typepath.path) => {
521 let type_params = &typepath.path.segments.iter().next().unwrap().arguments;
523 let generic_arg = match type_params {
525 PathArguments::AngleBracketed(params) => Some(params.args.iter().next().unwrap()),
526 _ => None,
527 }?;
528 match generic_arg {
530 GenericArgument::Type(ty) => Some(ty),
531 _ => None,
532 }
533 }
534 _ => None,
535 }
536}