macro_field_utils/
fields.rs

1use darling::ast::{Fields, Style};
2use proc_macro2::TokenStream;
3use quote::{format_ident, quote, ToTokens};
4
5/// Utility macro to implement [FieldInfo]
6#[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
25/// Trait to retrieve fields' properties
26pub trait FieldInfo {
27    /// Retrieves the identifier of the passed-in field, or [None] for tuple fields
28    fn ident(&self) -> &Option<syn::Ident>;
29    /// Retrieves the visibility of the passed-in field
30    fn vis(&self) -> &syn::Visibility;
31    /// Retrieves the type of the passed-in field
32    fn ty(&self) -> &syn::Type;
33    /// Retrieves the ident of the field or a `v_{ix}` ident based on the field index
34    fn as_ident(&self, ix: usize) -> syn::Ident {
35        self.ident().clone().unwrap_or_else(|| format_ident!("v_{ix}"))
36    }
37}
38
39/// Utility struct to work with [Fields]
40pub 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    /// Builds a new [FieldsHelper]
56    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    /// Remove all fields `f` for which `predicate(&f)` returns `false`.
73    /// This method operates in place, visiting each element exactly once in the
74    /// original order, and preserves the order of the retained elements.
75    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    /// Adds an arbitrary number of attributes to each field.
84    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    /// Includes additional default fields by including `field1: Default::default(), field2: Default::default(),`
93    ///
94    /// It's only used on named fields, but ignored for tuples.
95    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    /// Includes additional fields by including `field1: field1, field2: field2,`
105    ///
106    /// It's only used on named fields, but ignored for tuples.
107    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    /// Includes additional fields by including `field1: expr1, field2: expr2`.
117    ///
118    /// It's only used on named fields, but ignored for tuples.
119    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    /// Ignore extra fields by including `, field1: _ , field2: _` at the end.
129    ///
130    /// It's only used on named fields, but ignored for tuples.
131    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    /// Wether to include `, ..` at the end or not, defaults to `false`.
138    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    /// Wether to include `, ..Default::default()` at the end or not, defaults to `false`.
144    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    /// Wether to include the visibility at the beginning or not, defaults to `false`.
150    pub fn include_visibility(mut self, include_visibility: bool) -> Self {
151        self.include_visibility = include_visibility;
152        self
153    }
154
155    /// Wether to include the wrapper (curly braces for named fields, parenthesis for tuples), defaults to `true`.
156    pub fn include_wrapper(mut self, include_wrapper: bool) -> Self {
157        self.include_wrapper = include_wrapper;
158        self
159    }
160
161    /// Specifies the left collector. It's only used with named fields.
162    ///
163    /// Defaults to `FieldsCollector::ident`
164    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    /// Specifies the right collector.
173    ///
174    /// Defaults to `FieldsCollector::ty`
175    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    /// Checks if there's no fields
184    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    /// Takes the inner fields
200    pub fn into_vec(self) -> Vec<&'f T> {
201        self.fields.fields
202    }
203
204    /// Collects the fields.
205    ///
206    /// # Examples
207    /// If using the default collectors on a Struct:
208    /// ``` ignore
209    /// {
210    ///     field_1: String,
211    ///     field_2: i32,
212    /// }
213    /// ```
214    ///
215    /// If using `FieldsCollector::ident` right collector on a Struct:
216    /// ``` ignore
217    /// {
218    ///     field_1: field_1,
219    ///     field_2: field_2,
220    /// }
221    /// ```
222    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
345/// Utility struct with common collectors for [FieldsHelper]
346pub struct FieldsCollector;
347impl FieldsCollector {
348    // Collects just the type of the field
349    pub fn ty<T: FieldInfo>(_ix: usize, t: &T) -> TokenStream {
350        let ty = t.ty();
351
352        quote!(#ty)
353    }
354
355    // Collects just the ident of the field
356    pub fn ident<T: FieldInfo>(ix: usize, t: &T) -> TokenStream {
357        let ident = t.as_ident(ix);
358
359        quote!(#ident)
360    }
361
362    // Collects just `Default::default()`
363    pub fn default<T: FieldInfo>(_ix: usize, _t: &T) -> TokenStream {
364        quote!(Default::default())
365    }
366
367    // Collects just `_`
368    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)] // darling macro
376
377    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        /// The identifier of the passed-in field, or [None] for tuple fields
387        ident: Option<syn::Ident>,
388        /// The visibility of the passed-in field
389        vis: syn::Visibility,
390        /// The type of the passed-in field
391        ty: syn::Type,
392
393        /// Whether to skip
394        #[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}