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()] ); } }