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
#![doc = include_str!("../README.md")]
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::token::Comma;
use syn::{
parse_macro_input, AttrStyle, Data, DeriveInput, Fields, Meta,
NestedMeta,
};
const ERR_MSG: &str =
"Derive(FieldNamesAsArray) only applicable to named structs";
#[proc_macro_derive(
FieldNamesAsArray,
attributes(field_names_as_array)
)]
pub fn derive_field_names_as_array(
input: TokenStream,
) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let vis = &input.vis;
let (impl_generics, type_generics, where_clause) =
&input.generics.split_for_impl();
let field_names: Punctuated<String, Comma> = match input.data {
Data::Struct(data_struct) => match data_struct.fields {
Fields::Named(fields) => fields
.named
.into_iter()
.filter_map(|f| {
for attr in f.attrs.iter() {
match attr.style {
AttrStyle::Outer => {}
_ => continue,
}
let attr_name = attr
.path
.segments
.iter()
.last()
.cloned()
.expect("attribute is badly formatted");
if attr_name.ident != "field_names_as_array" {
continue;
}
let meta = attr
.parse_meta()
.expect("cannot parse attribute to meta");
let list = match meta {
Meta::List(l) => l,
_ => panic!("field_names_as_array needs an argument"),
};
let arg = list
.nested
.iter()
.next()
.expect("argument list cannot be empty");
match arg {
NestedMeta::Meta(m) => match m.path().get_ident() {
Some(i) if i == "skip" => return None,
_ => panic!("unknown argument"),
},
_ => panic!("badly formatted argument"),
}
}
Some(f.ident.unwrap().to_string())
})
.collect(),
_ => panic!("{}", ERR_MSG),
},
_ => panic!("{}", ERR_MSG),
};
let result = quote! {
impl #impl_generics #name #type_generics #where_clause {
#[doc=concat!("Generated array of field names for `", stringify!(#name #type_generics), "`.")]
#vis const FIELD_NAMES_AS_ARRAY: &'static [&'static str] =
&[#field_names];
}
};
TokenStream::from(result)
}