mysqldump_quick_xml_derive/
lib.rs1#![recursion_limit = "256"]
15extern crate proc_macro;
16
17use proc_macro2::TokenStream;
18use quote::quote;
19use syn::{parse_macro_input, Data, DeriveInput, Fields};
20
21#[proc_macro_derive(MysqlDumpQuickXml)]
22pub fn mysqldump_quick_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23 let input = parse_macro_input!(input as DeriveInput);
24
25 let name = &input.ident;
26
27 let (fields_declare, fields_set, fields_match_and_set_current, fields_set_none) =
28 mysqldump_quick_xml_fields(&input.data);
29
30 let expanded = quote! {
31
32 impl ::mysqldump_quick_xml::MysqlDumpQuickXml for #name {
33 fn from_str(xml: &str) -> Vec<Self> {
34 use ::mysqldump_quick_xml::quick_xml::Reader;
35 use ::mysqldump_quick_xml::quick_xml::events::Event;
36
37 let mut reader = Reader::from_str(xml);
38 reader.trim_text(true);
39
40 let mut buf = Vec::new();
41
42 let mut rows = vec![];
43
44 #fields_declare
45
46 let mut current_field = None;
47
48 loop {
49 match reader.read_event(&mut buf) {
50 Ok(Event::Start(ref e)) => match e.name() {
51 b"field" => {
52 let field = e
53 .attributes()
54 .map(|x| x.unwrap())
55 .filter(|x| x.key == b"name")
56 .next()
57 .map(|x| x.value)
58 .unwrap();
59 match field.as_ref() {
60 #fields_match_and_set_current
61 _ => (),
62 }
63 }
64 _ => (),
65 },
66 Ok(Event::End(ref e)) => match e.name() {
67 b"row" => {
68 current_field = None;
69 rows.push(Self {
70 #fields_set
71 });
72 #fields_set_none
73 }
74 b"field" => {
75 current_field = None;
76 }
77 _ => (),
78 },
79 Ok(Event::Text(e)) => {
80 if let Some(field) = &mut current_field {
81 field.replace(e.unescape_and_decode(&reader).unwrap());
82 }
83 }
84 Ok(Event::Eof) => break,
85 Err(e) => panic!("panic at {}: {:?}", reader.buffer_position(), e),
86 _ => (),
87 }
88
89 buf.clear();
90 }
91
92 rows
93 }
94 }
95
96 };
97
98 expanded.into()
99}
100
101fn mysqldump_quick_xml_fields(data: &Data) -> (TokenStream, TokenStream, TokenStream, TokenStream) {
102 match *data {
103 Data::Struct(ref data) => match data.fields {
104 Fields::Named(ref fields) => {
105 let fields_declare = fields.named.iter().map(|f| {
106 let name = &f.ident;
107 quote! {
108 let mut #name: Option<String> = None;
109 }
110 });
111 let fields_set = fields.named.iter().map(|f| {
112 let name = &f.ident;
113 quote! {
114 #name: #name.clone().unwrap_or_default().into(),
115 }
116 });
117 let fields_match_and_set_current = fields.named.iter().map(|f| {
118 let name = f.ident.as_ref().unwrap();
119 let concatenated = format!("{}", name);
120 let varname = syn::LitByteStr::new(concatenated.as_bytes(), name.span());
121
122 quote! {
123 #varname => current_field = Some(&mut #name),
124 }
125 });
126 let fields_set_null = fields.named.iter().map(|f| {
127 let name = &f.ident;
128 quote! {
129 #name = None;
130 }
131 });
132
133 (
134 quote! {
135 #(#fields_declare)*
136 },
137 quote! {
138 #(#fields_set)*
139 },
140 quote! {
141 #(#fields_match_and_set_current)*
142 },
143 quote! {
144 #(#fields_set_null)*
145 },
146 )
147 }
148 Fields::Unnamed(_) => unimplemented!(),
149 Fields::Unit => unimplemented!(),
150 },
151 Data::Enum(_) | Data::Union(_) => unimplemented!(),
152 }
153}