1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
extern crate proc_macro;

use darling::{ast::Data, FromDeriveInput, FromField};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, DeriveInput, Generics, Ident};

#[proc_macro_derive(FieldNames, attributes(field_names))]
pub fn derive_field_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    Receiver::from_derive_input(&parse_macro_input!(input as DeriveInput))
        .map(|receiver| quote!(#receiver))
        .unwrap_or_else(|err| err.write_errors())
        .into()
}

#[derive(FromDeriveInput)]
#[darling(supports(struct_named))]
struct Receiver {
    ident: Ident,
    generics: Generics,
    data: Data<(), ReceiverField>,
}

impl Receiver {
    fn fields_to_emit(&self) -> Vec<String> {
        self.data
            .as_ref()
            .take_struct()
            .expect("FieldNames only supports named structs")
            .into_iter()
            .filter(|field| !field.skip)
            .map(|field| field.name())
            .collect()
    }
}

impl ToTokens for Receiver {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let ident = &self.ident;
        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
        let fields = self.fields_to_emit();
        let fields_len = fields.len();

        tokens.extend(quote! {
            #[automatically_derived]
            impl #impl_generics #ident #ty_generics #where_clause {
                const FIELDS: [&'static str; #fields_len] = [
                    #(#fields),*
                ];
            }
        })
    }
}

#[derive(FromField)]
#[darling(attributes(field_names))]
struct ReceiverField {
    ident: Option<Ident>,
    #[darling(default)]
    skip: bool,
}

impl ReceiverField {
    fn name(&self) -> String {
        self.ident
            .as_ref()
            .expect("FieldNames only supports named fields")
            .to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::Receiver;
    use darling::FromDeriveInput;
    use syn::parse_quote;

    #[test]
    fn simple() {
        let input = Receiver::from_derive_input(&parse_quote! {
            #[derive(FieldNames)]
            struct Example {
                hello: String,
                world: String,
            }
        })
        .unwrap();

        assert_eq!(
            input.fields_to_emit(),
            vec!["hello".to_string(), "world".to_string()]
        );
    }

    #[test]
    fn skip_field() {
        let input = Receiver::from_derive_input(&parse_quote! {
            #[derive(FieldNames)]
            struct Example {
                hello: String,
                #[field_names(skip)]
                hidden: bool,
                world: String,
            }
        })
        .unwrap();

        assert_eq!(
            input.fields_to_emit(),
            vec!["hello".to_string(), "world".to_string()]
        );
    }
}