xml_attributes_derive/
lib.rs1extern crate proc_macro;
2extern crate proc_macro2;
20#[macro_use]
21extern crate quote;
22extern crate syn;
23
24use proc_macro::TokenStream;
25use proc_macro2::{Literal, Spacing, Span, TokenNode, TokenTree};
26use quote::ToTokens;
27use syn::{Data, Ident, Type};
28
29#[proc_macro_derive(FromXmlAttributes)]
30pub fn from_xml_attributes(input: TokenStream) -> TokenStream {
31 let ast = syn::parse(input).unwrap();
33
34 let gen = impl_xml(&ast);
36
37 gen.into()
39}
40
41fn impl_xml(ast: &syn::DeriveInput) -> quote::Tokens {
42 let name = &ast.ident;
43 match ast.data {
44 Data::Struct(ref data) => impl_struct_xml_fields(name, &data.fields),
45 Data::Enum(ref data) => enum_fields(name, data),
46 Data::Union(ref data) => union_fields(name, data),
47 }
48}
49
50fn union_fields(_name: &syn::Ident, _data: &syn::DataUnion) -> quote::Tokens {
51 let mut result = Vec::new();
52 result.push(quote! {
53 panic!("not implemented");
54 });
55 quote! {
56 #(#result)*
57 }
58}
59
60fn enum_fields(_name: &syn::Ident, _variants: &syn::DataEnum) -> quote::Tokens {
61 let mut result = Vec::new();
62 result.push(quote! {
63 panic!("not implemented");
64 });
65 quote! {
66 #(#result)*
67 }
68}
69
70fn impl_struct_xml_fields(name: &syn::Ident, fields: &syn::Fields) -> quote::Tokens {
71 let mut result = Vec::new();
72 for field in fields.iter() {
73 let ident = &field.ident;
74 let ident_type = match field.clone().ty {
75 Type::Path(p) => {
76 if let Some(i) = p.path.segments.clone().into_iter().next() {
77 Some(i.ident)
78 } else {
79 None
80 }
81 }
82 _ => None,
83 };
84 let u_64 = Ident::new("u64", Span::def_site());
85 let f_64 = Ident::new("f64", Span::def_site());
86 let string = Ident::new("String", Span::def_site());
87 let boolean = Ident::new("bool", Span::def_site());
88
89 match ident_type {
91 Some(i_type) => {
92 if i_type == u_64 {
93 result.push(quote! {
94 let mut #ident = 0;
95 });
96 } else if i_type == f_64 {
97 result.push(quote! {
98 let mut #ident = 0.0;
99 });
100 } else if i_type == string {
101 result.push(quote! {
102 let mut #ident = String::new();
103 });
104 } else if i_type == boolean {
105 result.push(quote! {
106 let mut #ident = false;
107 });
108 } else {
109 }
112 }
113 None => {
114 println!("Unable to identify type for {:?}", ident);
116 }
117 }
118 }
119
120 result.push(quote! {
122 for a in attrs
123 });
124
125 result.push(
126 TokenTree {
127 span: Span::def_site(),
128 kind: TokenNode::Op('{', Spacing::Joint),
129 }
130 .into_tokens(),
131 );
132
133 result.push(quote! {
134 let item = a?;
135 let val = String::from_utf8_lossy(&item.value);
136 match item.key
137 });
138
139 result.push(
140 TokenTree {
141 span: Span::def_site(),
142 kind: TokenNode::Op('{', Spacing::Joint),
143 }
144 .into_tokens(),
145 );
146
147 for field in fields.iter() {
148 let ident = &field.ident;
149 let ident_type = match field.clone().ty {
150 Type::Path(p) => {
151 if let Some(i) = p.path.segments.clone().into_iter().next() {
152 Some(i.ident)
153 } else {
154 None
155 }
156 }
157 _ => None,
158 };
159 let u_64 = Ident::new("u64", Span::def_site());
160 let f_64 = Ident::new("f64", Span::def_site());
161 let string = Ident::new("String", Span::def_site());
162 let boolean = Ident::new("bool", Span::def_site());
163
164 let i = ident.unwrap();
165 let ident_name = {
166 let i = i.as_ref();
167 if i.starts_with("_") {
168 i.trim_start_matches("_")
169 } else {
170 i
171 }
172 };
173
174 match ident_type {
175 Some(i_type) => {
176 if i_type == u_64 {
177 result.push(
178 TokenTree {
179 span: Span::def_site(),
180 kind: TokenNode::Literal(Literal::byte_string(ident_name.as_bytes())),
181 }
182 .into_tokens(),
183 );
184
185 result.push(quote! {
186 => {
187 #ident = u64::from_str(&val)?;
188 }
189 });
190 } else if i_type == f_64 {
191 result.push(
192 TokenTree {
193 span: Span::def_site(),
194 kind: TokenNode::Literal(Literal::byte_string(ident_name.as_bytes())),
195 }
196 .into_tokens(),
197 );
198
199 result.push(quote! {
200 => {
201 #ident = f64::from_str(&val)?;
202 }
203 });
204 } else if i_type == string {
205 result.push(
206 TokenTree {
207 span: Span::def_site(),
208 kind: TokenNode::Literal(Literal::byte_string(ident_name.as_bytes())),
209 }
210 .into_tokens(),
211 );
212
213 result.push(quote! {
214 => {
215 #ident = val.to_string();
216 }
217 });
218 } else if i_type == boolean {
219 result.push(
220 TokenTree {
221 span: Span::def_site(),
222 kind: TokenNode::Literal(Literal::byte_string(ident_name.as_bytes())),
223 }
224 .into_tokens(),
225 );
226
227 result.push(quote! {
228 => {
229 #ident = bool::from_str(&val)?;
230 }
231 });
232 } else {
233 println!("else: {:?} {:?}", ident, i_type);
235 }
236 }
237 None => {
238 println!("Unable to identify type for {:?}", ident);
240 }
241 }
242 }
243
244 result.push(quote! {
245 _ => {
246 debug!(
247 "unknown xml attribute: {}",
248 String::from_utf8_lossy(item.key)
249 );
250 }
251 });
252
253 result.push(
254 TokenTree {
255 span: Span::def_site(),
256 kind: TokenNode::Op('}', Spacing::Joint),
257 }
258 .into_tokens(),
259 );
260 result.push(
261 TokenTree {
262 span: Span::def_site(),
263 kind: TokenNode::Op('}', Spacing::Joint),
264 }
265 .into_tokens(),
266 );
267 result.push(quote! {
268 Ok
269 });
270
271 result.push(
272 TokenTree {
273 span: Span::def_site(),
274 kind: TokenNode::Op('(', Spacing::Joint),
275 }
276 .into_tokens(),
277 );
278
279 result.push(quote! {
280 #name
281 });
282
283 result.push(
284 TokenTree {
285 span: Span::def_site(),
286 kind: TokenNode::Op('{', Spacing::Joint),
287 }
288 .into_tokens(),
289 );
290
291 for field in fields.iter() {
292 let ident = &field.ident;
293
294 result.push(quote! {
295 #ident: #ident,
296 });
297 }
298
299 result.push(
300 TokenTree {
301 span: Span::def_site(),
302 kind: TokenNode::Op('}', Spacing::Joint),
303 }
304 .into_tokens(),
305 );
306 result.push(
307 TokenTree {
308 span: Span::def_site(),
309 kind: TokenNode::Op(')', Spacing::Joint),
310 }
311 .into_tokens(),
312 );
313
314 quote! {
315 impl FromXmlAttributes for #name {
316 fn from_xml_attributes(attrs: Attributes) -> MetricsResult<Self> {
317 #(#result)*
318 }
319 }
320 }
321}