1extern crate proc_macro;
2extern crate proc_macro2;
3
4use proc_macro2::{TokenStream, TokenTree};
5use quote::quote;
6use syn::{parse_macro_input, DeriveInput, Ident, Type, Meta};
7
8struct AliasMapping {
9 alias: Ident,
10 resolve: Ident,
11}
12
13#[proc_macro_derive(NameIndex, attributes(index, alias))]
14pub fn derive_name_index(input: proc_macro::TokenStream)
15 -> proc_macro::TokenStream {
16 let input = parse_macro_input!(input as DeriveInput);
17 let struct_ident = input.ident;
18
19 macro_rules! err {
20 ($msg:expr) => {
21 return syn::Error::new_spanned(&struct_ident, $msg)
22 .into_compile_error()
23 .into()
24 };
25 }
26
27 let fields = match input.data {
28 syn::Data::Struct(s) => s.fields,
29 _ => err!("NameIndex can only be derived for structs!"),
30 };
31
32 let fields = match fields {
33 syn::Fields::Named(f) => f.named,
34 _ => err!("Only named fields are supported for NameIndex!"),
35 };
36
37 if fields.len() < 1 {
38 err!("NameIndex derive requires at least one field!");
39 }
40
41 let mut field_idents: Vec<Ident> = vec![];
42
43 let mut aliases: Vec<AliasMapping> = vec![];
44 let mut aliases_field: Vec<(Ident, Vec<Ident>)> = vec![];
45
46 let mut ty: &Type = &fields[0].ty;
47 let mut found_index = false;
48 let mut index_present = false;
49
50 for i in 0..fields.len() {
51 let f = &fields[i];
52 for attr in f.attrs.iter() {
53 if attr.path().get_ident().unwrap().to_string() == "index" {
54 index_present = true;
55 break;
56 }
57 }
58 if index_present {
59 break;
60 }
61 }
62
63 for i in 0..fields.len() {
64 let f = &fields[i];
65 let ident = f.ident.clone().unwrap();
66
67 if index_present {
68 for attr in f.attrs.iter() {
69 if attr.path().get_ident().unwrap().to_string() == "index" {
70 if found_index {
72 err!("Only one field can hold the index attribute!");
73 }
74
75 found_index = true;
76 ty = &f.ty;
77 break;
78 }
79 }
80 if !found_index {
82 continue;
83 }
84 }
85
86 if f.ty != *ty {
87 continue;
88 }
89
90 field_idents.push(ident.clone());
91
92 for attr in f.attrs.iter() {
93 if attr.path().get_ident().unwrap().to_string() == "alias" {
94 let list = match &attr.meta {
95 Meta::List(l) => l,
96 _ => {
97 err!("Invalid alias attribute! Expected MetaList");
98 },
99 };
100 let tokens: Vec<TokenTree> = list.tokens
101 .clone()
102 .into_iter()
103 .collect();
104 let mut field_aliases: Vec<Ident> = vec![];
105 let mut found_delim = true;
106 for tt in tokens {
107 if found_delim {
108 match tt {
109 TokenTree::Ident(id) => {
110 field_aliases.push(id);
111 },
112 _ => {
113 err!("Invalid alias attribute!");
114 },
115 }
116 found_delim = false;
117 }
118 else {
119 match tt {
120 TokenTree::Punct(p) => {
121 if p.as_char() != ',' {
122 err!("Invalid alias attribute!");
123 }
124 found_delim = true;
125 }
126 _ => {
127 err!("Invalid alias attribute!");
128 },
129 }
130 }
131 }
132 for alias in &field_aliases {
133 aliases.push(AliasMapping {
134 alias: alias.clone(),
135 resolve: ident.clone(),
136 });
137 }
138 aliases_field.push((ident.clone(), field_aliases));
139 break;
140 }
141 }
142 }
143
144 let mut aliases_ref = TokenStream::new();
145 for m in &aliases {
146 let alias = &m.alias;
147 let resolve = &m.resolve;
148 let tok: TokenStream = quote! {
149 ::std::stringify!(#alias) =>
150 ::std::option::Option::Some(&self.#resolve),
151 }.into();
152 aliases_ref.extend(tok);
153 }
154
155 let mut aliases_ref_mut = TokenStream::new();
156 for m in &aliases {
157 let alias = &m.alias;
158 let resolve = &m.resolve;
159 let tok: TokenStream = quote! {
160 ::std::stringify!(#alias) =>
161 ::std::option::Option::Some(&mut self.#resolve),
162 }.into();
163 aliases_ref_mut.extend(tok);
164 }
165
166 let mut field_aliases = TokenStream::new();
167 for (ident, aliases) in aliases_field {
168 let v: TokenStream = quote! {
169 ::std::vec![#(::std::stringify!(#aliases)),*]
170 }.into();
171 let tok: TokenStream = quote! {
172 ::std::stringify!(#ident) => #v,
173 }.into();
174 field_aliases.extend(tok);
175 }
176
177 let mut resolve_aliases = TokenStream::new();
178 for m in &aliases {
179 let alias = &m.alias;
180 let resolve = &m.resolve;
181 let tok: TokenStream = quote! {
182 ::std::stringify!(#alias) =>
183 ::std::option::Option::Some(::std::stringify!(#resolve)),
184 }.into();
185 resolve_aliases.extend(tok);
186 }
187
188 let tok = quote! {
189 impl ::name_index::NameIndex<#ty> for #struct_ident {
190 fn get_ref(&self, name: &str) -> ::std::option::Option<&#ty> {
191 match name {
192 #(::std::stringify!(#field_idents) =>
193 ::std::option::Option::Some(&self.#field_idents),)*
194 #aliases_ref
195 _ => ::std::option::Option::None,
196 }
197 }
198
199 fn get_ref_mut(&mut self, name: &str) ->
200 ::std::option::Option<&mut #ty> {
201 match name {
202 #(::std::stringify!(#field_idents) =>
203 ::std::option::Option::Some(&mut self.#field_idents),)*
204 #aliases_ref_mut
205 _ => ::std::option::Option::None,
206 }
207 }
208
209 fn fields(&self) ->
210 ::std::vec::Vec<::name_index::Field::<#ty>> {
211 ::std::vec![#((::std::stringify!(#field_idents),
212 &self.#field_idents)),*]
213 }
214
215 fn fields_mut(&mut self) ->
216 ::std::vec::Vec<::name_index::FieldMut::<#ty>> {
217 ::std::vec![#((::std::stringify!(#field_idents),
218 &mut self.#field_idents)),*]
219 }
220
221 fn field_aliases(&self, name: &str) ->
222 ::std::vec::Vec<&'static str> {
223 match name {
224 #field_aliases
225 _ => ::std::vec![],
226 }
227 }
228
229 fn resolve_alias(&self, name: &str) ->
230 ::std::option::Option<&'static str> {
231 match name {
232 #(::std::stringify!(#field_idents) =>
233 ::std::option::Option::Some(
234 ::std::stringify!(#field_idents)
235 ),)*
236 #resolve_aliases
237 _ => ::std::option::Option::None,
238 }
239 }
240 }
241 };
242
243 tok.into()
246}