1use darling::ast::{Fields, Style};
2use proc_macro2::TokenStream;
3use quote::{format_ident, quote, ToTokens};
4
5#[macro_export]
7macro_rules! field_info {
8 ($f:path) => {
9 impl $crate::FieldInfo for $f {
10 fn ident(&self) -> &Option<syn::Ident> {
11 &self.ident
12 }
13
14 fn vis(&self) -> &syn::Visibility {
15 &self.vis
16 }
17
18 fn ty(&self) -> &syn::Type {
19 &self.ty
20 }
21 }
22 };
23}
24
25pub trait FieldInfo {
27 fn ident(&self) -> &Option<syn::Ident>;
29 fn vis(&self) -> &syn::Visibility;
31 fn ty(&self) -> &syn::Type;
33 fn as_ident(&self, ix: usize) -> syn::Ident {
35 self.ident().clone().unwrap_or_else(|| format_ident!("v_{ix}"))
36 }
37}
38
39pub struct FieldsHelper<'f, T: FieldInfo> {
41 fields: Fields<&'f T>,
42 filter: Option<Box<dyn Fn(usize, &T) -> bool + 'f>>,
43 attributes: Option<Box<dyn Fn(usize, &T) -> Option<TokenStream> + 'f>>,
44 extra_fields: Vec<TokenStream>,
45 include_all_default: bool,
46 ignore_extra: Vec<syn::Ident>,
47 ignore_all_extra: bool,
48 include_visibility: bool,
49 include_wrapper: bool,
50 left_collector: Option<Box<dyn FnMut(usize, &T) -> TokenStream + 'f>>,
51 right_collector: Option<Box<dyn FnMut(usize, &T) -> TokenStream + 'f>>,
52}
53
54impl<'f, T: FieldInfo> FieldsHelper<'f, T> {
55 pub fn new(fields: &'f Fields<T>) -> Self {
57 Self {
58 fields: fields.as_ref(),
59 filter: None,
60 attributes: None,
61 extra_fields: Vec::new(),
62 include_all_default: false,
63 ignore_extra: Vec::new(),
64 ignore_all_extra: false,
65 include_visibility: false,
66 include_wrapper: true,
67 left_collector: None,
68 right_collector: None,
69 }
70 }
71
72 pub fn filtering<P>(mut self, predicate: P) -> Self
76 where
77 P: Fn(usize, &T) -> bool + 'f,
78 {
79 self.filter = Some(Box::new(predicate));
80 self
81 }
82
83 pub fn with_attributes<P>(mut self, predicate: P) -> Self
85 where
86 P: Fn(usize, &T) -> Option<TokenStream> + 'f,
87 {
88 self.attributes = Some(Box::new(predicate));
89 self
90 }
91
92 pub fn extra_default_fields<'a>(mut self, fields: impl IntoIterator<Item = &'a syn::Ident>) -> Self {
96 let mut default_fields = fields
97 .into_iter()
98 .map(|field| quote!(#field: Default::default()))
99 .collect::<Vec<_>>();
100 self.extra_fields.append(&mut default_fields);
101 self
102 }
103
104 pub fn extra_fields<'a>(mut self, fields: impl IntoIterator<Item = &'a syn::Ident>) -> Self {
108 let mut additional_fields = fields
109 .into_iter()
110 .map(|field| quote!(#field: #field))
111 .collect::<Vec<_>>();
112 self.extra_fields.append(&mut additional_fields);
113 self
114 }
115
116 pub fn extra_fields_with<'a>(mut self, fields: impl IntoIterator<Item = (&'a syn::Ident, impl ToTokens)>) -> Self {
120 let mut additional_fields = fields
121 .into_iter()
122 .map(|(field, expr)| quote!(#field: #expr))
123 .collect::<Vec<_>>();
124 self.extra_fields.append(&mut additional_fields);
125 self
126 }
127
128 pub fn ignore_extra<'a>(mut self, ignore_extra: impl IntoIterator<Item = &'a syn::Ident>) -> Self {
132 let mut ignore_extra = ignore_extra.into_iter().cloned().collect::<Vec<_>>();
133 self.ignore_extra.append(&mut ignore_extra);
134 self
135 }
136
137 pub fn ignore_all_extra(mut self, ignore_all_extra: bool) -> Self {
139 self.ignore_all_extra = ignore_all_extra;
140 self
141 }
142
143 pub fn include_all_default(mut self, include_all_default: bool) -> Self {
145 self.include_all_default = include_all_default;
146 self
147 }
148
149 pub fn include_visibility(mut self, include_visibility: bool) -> Self {
151 self.include_visibility = include_visibility;
152 self
153 }
154
155 pub fn include_wrapper(mut self, include_wrapper: bool) -> Self {
157 self.include_wrapper = include_wrapper;
158 self
159 }
160
161 pub fn left_collector<C>(mut self, left_collector: C) -> Self
165 where
166 C: FnMut(usize, &T) -> TokenStream + 'f,
167 {
168 self.left_collector = Some(Box::new(left_collector));
169 self
170 }
171
172 pub fn right_collector<C>(mut self, right_collector: C) -> Self
176 where
177 C: FnMut(usize, &T) -> TokenStream + 'f,
178 {
179 self.right_collector = Some(Box::new(right_collector));
180 self
181 }
182
183 pub fn is_empty(&self) -> bool {
185 if self.fields.is_empty() {
186 true
187 } else if let Some(filter_fn) = &self.filter {
188 self.fields
189 .iter()
190 .enumerate()
191 .filter(|&(ix, f)| filter_fn(ix, f))
192 .collect::<Vec<_>>()
193 .is_empty()
194 } else {
195 false
196 }
197 }
198
199 pub fn into_vec(self) -> Vec<&'f T> {
201 self.fields.fields
202 }
203
204 pub fn collect(mut self) -> TokenStream {
223 let mut tokens = TokenStream::new();
224 match self.fields.style {
225 Style::Unit => (),
226 Style::Tuple => {
227 let mut right_collector = self.right_collector.unwrap_or_else(|| Box::new(FieldsCollector::ty));
228
229 let fields = self
230 .fields
231 .into_iter()
232 .enumerate()
233 .filter(|&(ix, f)| {
234 if let Some(filter_fn) = &mut self.filter {
235 filter_fn(ix, f)
236 } else {
237 true
238 }
239 })
240 .map(|(ix, f)| {
241 let vis = f.vis();
242 let collected_field = right_collector(ix, f);
243 let attrs = if let Some(attrs_fn) = &mut self.attributes {
244 attrs_fn(ix, f)
245 } else {
246 None
247 }
248 .unwrap_or_default();
249 if self.include_visibility {
250 quote!( #attrs #vis #collected_field )
251 } else {
252 quote!( #attrs #collected_field )
253 }
254 });
255
256 if self.include_all_default {
257 if self.include_wrapper {
258 quote!( ( #( #fields ),* , ..Default::default() ) ).to_tokens(&mut tokens);
259 } else {
260 quote!( #( #fields ),* , ..Default::default() ).to_tokens(&mut tokens);
261 }
262 } else if self.ignore_all_extra {
263 if self.include_wrapper {
264 quote!( ( #( #fields ),* , .. ) ).to_tokens(&mut tokens);
265 } else {
266 quote!( #( #fields ),* , .. ).to_tokens(&mut tokens);
267 }
268 } else if self.include_wrapper {
269 quote!( ( #( #fields ),* ) ).to_tokens(&mut tokens);
270 } else {
271 quote!( #( #fields ),* ).to_tokens(&mut tokens);
272 }
273 }
274 Style::Struct => {
275 let mut left_collector = self.left_collector.unwrap_or_else(|| Box::new(FieldsCollector::ident));
276 let mut right_collector = self.right_collector.unwrap_or_else(|| Box::new(FieldsCollector::ty));
277
278 let mut fields = self.extra_fields;
279
280 fields.extend(
281 self.fields
282 .into_iter()
283 .enumerate()
284 .filter(|&(ix, f)| {
285 if let Some(filter_fn) = &mut self.filter {
286 filter_fn(ix, f)
287 } else {
288 true
289 }
290 })
291 .map(|(ix, f)| {
292 let id = left_collector(ix, f);
293 let vis = f.vis();
294 let collected_field = right_collector(ix, f);
295 let attrs = if let Some(attrs_fn) = &mut self.attributes {
296 attrs_fn(ix, f)
297 } else {
298 None
299 }
300 .unwrap_or_default();
301 if self.include_visibility {
302 quote!(
303 #attrs
304 #vis #id: #collected_field
305 )
306 } else {
307 quote!(
308 #attrs
309 #id: #collected_field
310 )
311 }
312 }),
313 );
314
315 let mut ignore_extra = self
316 .ignore_extra
317 .into_iter()
318 .map(|field| quote!(#field: _))
319 .collect::<Vec<_>>();
320 fields.append(&mut ignore_extra);
321
322 if self.include_all_default {
323 if self.include_wrapper {
324 quote!( { #( #fields ),* , ..Default::default() } ).to_tokens(&mut tokens);
325 } else {
326 quote!( #( #fields ),* , ..Default::default() ).to_tokens(&mut tokens);
327 }
328 } else if self.ignore_all_extra {
329 if self.include_wrapper {
330 quote!( { #( #fields ),* , .. } ).to_tokens(&mut tokens);
331 } else {
332 quote!( #( #fields ),* , .. ).to_tokens(&mut tokens);
333 }
334 } else if self.include_wrapper {
335 quote!( { #( #fields ),* } ).to_tokens(&mut tokens);
336 } else {
337 quote!( #( #fields ),* ).to_tokens(&mut tokens);
338 }
339 }
340 }
341 tokens
342 }
343}
344
345pub struct FieldsCollector;
347impl FieldsCollector {
348 pub fn ty<T: FieldInfo>(_ix: usize, t: &T) -> TokenStream {
350 let ty = t.ty();
351
352 quote!(#ty)
353 }
354
355 pub fn ident<T: FieldInfo>(ix: usize, t: &T) -> TokenStream {
357 let ident = t.as_ident(ix);
358
359 quote!(#ident)
360 }
361
362 pub fn default<T: FieldInfo>(_ix: usize, _t: &T) -> TokenStream {
364 quote!(Default::default())
365 }
366
367 pub fn ignore<T: FieldInfo>(_ix: usize, _t: &T) -> TokenStream {
369 quote!(_)
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 #![allow(clippy::manual_unwrap_or_default)] use darling::FromField;
378 use quote::quote;
379 use syn::Result;
380
381 use super::*;
382
383 #[derive(FromField, Clone)]
384 #[darling(attributes(tst))]
385 struct FieldReceiver {
386 ident: Option<syn::Ident>,
388 vis: syn::Visibility,
390 ty: syn::Type,
392
393 #[darling(default)]
395 skip: bool,
396 }
397 field_info!(FieldReceiver);
398
399 #[test]
400 fn test_field_helper_named() -> Result<()> {
401 let fields: syn::FieldsNamed = syn::parse2(quote!({
402 pub field_1: String,
403 #[tst(skip)]
404 pub field_2: i32,
405 pub field_3: i64,
406 field_4: bool,
407 }))?;
408 let fields = Fields::<FieldReceiver>::try_from(&syn::Fields::Named(fields))?;
409
410 let collected = FieldsHelper::new(&fields).filtering(|_ix, f| !f.skip).collect();
411 #[rustfmt::skip]
412 let expected = quote!({
413 field_1: String,
414 field_3: i64,
415 field_4: bool
416 });
417
418 assert_eq!(collected.to_string(), expected.to_string());
419
420 let collected = FieldsHelper::new(&fields).include_visibility(true).collect();
421 #[rustfmt::skip]
422 let expected = quote!({
423 pub field_1: String,
424 pub field_2: i32,
425 pub field_3: i64,
426 field_4: bool
427 });
428
429 assert_eq!(collected.to_string(), expected.to_string());
430
431 let collected = FieldsHelper::new(&fields)
432 .filtering(|_ix, f| !f.skip)
433 .include_all_default(true)
434 .right_collector(FieldsCollector::ident)
435 .collect();
436 #[rustfmt::skip]
437 let expected = quote!({
438 field_1: field_1,
439 field_3: field_3,
440 field_4: field_4,
441 .. Default::default()
442 });
443
444 assert_eq!(collected.to_string(), expected.to_string());
445
446 let collected = FieldsHelper::new(&fields)
447 .filtering(|_ix, f| !f.skip)
448 .ignore_all_extra(true)
449 .right_collector(FieldsCollector::ident)
450 .collect();
451 #[rustfmt::skip]
452 let expected = quote!({
453 field_1: field_1,
454 field_3: field_3,
455 field_4: field_4,
456 ..
457 });
458
459 assert_eq!(collected.to_string(), expected.to_string());
460
461 let collected = FieldsHelper::new(&fields)
462 .with_attributes(|_ix, f| if f.skip { Some(quote!(#[skipped])) } else { None })
463 .collect();
464 #[rustfmt::skip]
465 let expected = quote!({
466 field_1: String,
467 #[skipped]
468 field_2: i32,
469 field_3: i64,
470 field_4: bool
471 });
472
473 assert_eq!(collected.to_string(), expected.to_string());
474
475 let collected = FieldsHelper::new(&fields)
476 .filtering(|_ix, f| !f.skip)
477 .right_collector(FieldsCollector::ident)
478 .extra_default_fields(fields.fields.iter().filter(|f| f.skip).filter_map(|f| f.ident.as_ref()))
479 .collect();
480 #[rustfmt::skip]
481 let expected = quote!({
482 field_2: Default::default(),
483 field_1: field_1,
484 field_3: field_3,
485 field_4: field_4
486 });
487
488 assert_eq!(collected.to_string(), expected.to_string());
489
490 let collected = FieldsHelper::new(&fields)
491 .filtering(|_ix, f| !f.skip)
492 .right_collector(FieldsCollector::ident)
493 .extra_fields(fields.fields.iter().filter(|f| f.skip).filter_map(|f| f.ident.as_ref()))
494 .collect();
495 #[rustfmt::skip]
496 let expected = quote!({
497 field_2: field_2,
498 field_1: field_1,
499 field_3: field_3,
500 field_4: field_4
501 });
502
503 assert_eq!(collected.to_string(), expected.to_string());
504
505 let collected = FieldsHelper::new(&fields)
506 .filtering(|_ix, f| !f.skip)
507 .right_collector(FieldsCollector::ident)
508 .extra_fields_with(
509 fields
510 .fields
511 .iter()
512 .filter(|f| f.skip)
513 .filter_map(|f| f.ident.as_ref().map(|ident| (ident, quote!(5)))),
514 )
515 .collect();
516 #[rustfmt::skip]
517 let expected = quote!({
518 field_2: 5,
519 field_1: field_1,
520 field_3: field_3,
521 field_4: field_4
522 });
523
524 assert_eq!(collected.to_string(), expected.to_string());
525
526 Ok(())
527 }
528
529 #[test]
530 fn test_field_helper_unnamed() -> Result<()> {
531 let fields: syn::FieldsUnnamed = syn::parse2(quote! {(
532 pub String,
533 #[tst(skip)]
534 pub i32,
535 pub i64,
536 bool,
537 )})?;
538 let fields = Fields::<FieldReceiver>::try_from(&syn::Fields::Unnamed(fields))?;
539
540 let collected = FieldsHelper::new(&fields).filtering(|_ix, f| !f.skip).collect();
541 #[rustfmt::skip]
542 let expected = quote!{(
543 String,
544 i64,
545 bool
546 )};
547
548 assert_eq!(collected.to_string(), expected.to_string());
549
550 let collected = FieldsHelper::new(&fields).include_visibility(true).collect();
551 #[rustfmt::skip]
552 let expected = quote!{(
553 pub String,
554 pub i32,
555 pub i64,
556 bool
557 )};
558
559 assert_eq!(collected.to_string(), expected.to_string());
560
561 let collected = FieldsHelper::new(&fields)
562 .filtering(|_ix, f| !f.skip)
563 .include_all_default(true)
564 .right_collector(FieldsCollector::ident)
565 .collect();
566 #[rustfmt::skip]
567 let expected = quote!{(
568 v_0,
569 v_2,
570 v_3,
571 .. Default::default()
572 )};
573
574 assert_eq!(collected.to_string(), expected.to_string());
575
576 let collected = FieldsHelper::new(&fields)
577 .filtering(|_ix, f| !f.skip)
578 .ignore_all_extra(true)
579 .right_collector(FieldsCollector::ident)
580 .collect();
581 #[rustfmt::skip]
582 let expected = quote!{(
583 v_0,
584 v_2,
585 v_3,
586 ..
587 )};
588
589 assert_eq!(collected.to_string(), expected.to_string());
590
591 let collected = FieldsHelper::new(&fields)
592 .with_attributes(|_ix, f| if f.skip { Some(quote!(#[skipped])) } else { None })
593 .collect();
594 #[rustfmt::skip]
595 let expected = quote!{(
596 String,
597 #[skipped]
598 i32,
599 i64,
600 bool
601 )};
602
603 assert_eq!(collected.to_string(), expected.to_string());
604
605 Ok(())
606 }
607}