runtime_struct_field_names_as_array/
lib.rs1#![doc = include_str!("../README.md")]
2
3extern crate proc_macro;
4use proc_macro::TokenStream;
5
6use quote::quote;
7use syn::{
8 parse_macro_input, DeriveInput, Meta, PathArguments, Type,
9};
10
11#[proc_macro_derive(
20 FieldNamesAsArray,
21 attributes(field_names_as_array)
22)]
23pub fn derive_field_names_as_array(
24 input: TokenStream,
25) -> TokenStream {
26 let input = parse_macro_input!(input as DeriveInput);
27
28 let struct_name = &input.ident;
29 let fields = match &input.data {
30 syn::Data::Struct(data) => &data.fields,
31 _ => panic!("FieldNamesAsArray can only be derived for structs"),
32 };
33
34 let mut field_exprs = Vec::new();
35
36 for field in fields.iter() {
37 let field_name = field.ident.as_ref().unwrap().to_string();
38
39 if let Some(attr) = field
40 .attrs
41 .iter()
42 .find(|a| a.path().is_ident("field_names_as_array"))
43 {
44 let field_type = &field.ty;
45 let nested_struct = if let Type::Path(type_path) = field_type {
46 if type_path.path.segments.last().unwrap().ident == "Option" {
47 if let PathArguments::AngleBracketed(arguments) =
48 &type_path.path.segments.last().unwrap().arguments
49 {
50 if let Some(generic_arg) = arguments.args.first() {
51 if let syn::GenericArgument::Type(inner_type) =
52 generic_arg
53 {
54 Some(inner_type)
55 } else {
56 None
57 }
58 } else {
59 None
60 }
61 } else {
62 None
63 }
64 } else {
65 Some(field_type)
66 }
67 } else {
68 None
69 };
70
71 let flatten: bool = match &attr.meta {
72 Meta::List(meta) => meta
73 .to_owned()
74 .tokens
75 .into_iter()
76 .find(|token| token.to_string() == "flatten")
77 .is_some(),
78 _ => false,
79 };
80
81 if flatten {
82 if let Some(nested_struct) = nested_struct {
83 field_exprs.push(quote! { <#nested_struct>::field_names_as_array().iter().map(|s| format!("{}.{}", #field_name, s)).collect::<Vec<_>>() });
84 }
85 } else {
86 field_exprs.push(quote! { vec![#field_name.to_string()] });
87 }
88 } else {
89 field_exprs.push(quote! { vec![#field_name.to_string()] });
90 }
91 }
92
93 let output = quote! {
94 impl #struct_name {
95 pub fn field_names_as_array() -> Vec<String> {
96 let mut field_names = Vec::new();
97 #( field_names.extend(#field_exprs); )*
98 field_names
99 }
100 }
101 };
102
103 output.into()
104}