1#![doc=include_str!("../README.md")]
2extern crate proc_macro;
3use proc_macro::TokenStream;
4use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
5use quote::{quote, ToTokens};
6use syn::{
7 braced,
8 ext::IdentExt as _,
9 parse::{Parse, ParseStream},
10 parse_macro_input,
11 spanned::Spanned,
12 token::{self, Brace},
13 Block, LitStr, Token,
14};
15
16struct Xmls {
17 events: Vec<XmlEvent>,
18}
19
20impl Parse for Xmls {
21 fn parse(input: ParseStream) -> syn::Result<Self> {
22 let mut events = Vec::new();
23 while !input.is_empty() {
24 events.push(input.parse()?)
25 }
26 Ok(Self { events })
27 }
28}
29
30enum XmlEvent {
32 Tag(XmlTag),
33 Text(Block),
34}
35
36impl Parse for XmlEvent {
37 fn parse(input: ParseStream) -> syn::Result<Self> {
38 let lookahead = input.lookahead1();
39 if lookahead.peek(Brace) {
40 Ok(Self::Text(input.parse()?))
41 } else {
42 Ok(Self::Tag(input.parse()?))
43 }
44 }
45}
46
47impl ToTokens for XmlEvent {
48 fn to_tokens(&self, tokens: &mut TokenStream2) {
49 match self {
50 XmlEvent::Tag(x) => x.to_tokens(tokens),
51 XmlEvent::Text(x) => quote! {
52 ::quick_xml::events::Event::Text (
53 ::quick_xml::events::BytesText::new(&*#x)
54 )
55 }
56 .to_tokens(tokens),
57 }
58 }
59}
60
61struct XmlTag {
63 #[allow(dead_code)]
65 open: Token![<],
66 is_end_tag: Option<Token![/]>,
67 name: XmlId,
68 attributes: Vec<XmlAttribute>,
69 self_close: Option<Token![/]>,
70 #[allow(dead_code)]
71 close: Token![>],
72}
73
74impl Parse for XmlTag {
75 fn parse(input: ParseStream) -> syn::Result<Self> {
76 Ok(Self {
77 open: input.parse()?,
78 is_end_tag: input.parse()?,
79 name: input.parse()?,
80 attributes: {
81 let mut vec = Vec::new();
82 loop {
83 let lookahead = input.lookahead1();
84 if lookahead.peek(Token![/]) || lookahead.peek(Token![>]) {
85 break;
86 } else {
87 vec.push(input.parse()?)
88 }
89 }
90 vec
91 },
92 self_close: input.parse()?,
93 close: input.parse()?,
94 })
95 }
96}
97
98impl ToTokens for XmlTag {
99 fn to_tokens(&self, tokens: &mut TokenStream2) {
100 let mut attrs = Vec::new();
101 for attribute in &self.attributes {
102 let name = attribute.key();
103 let val = attribute.value();
104 attrs.push(quote! {
105 event_start.push_attribute((#name, #val));
106 });
107 }
108
109 let name = self.name.to_lit_str();
110 let expanded = if self.is_end_tag.is_some() {
111 if self.self_close.is_some() {
112 quote! { compile_error!("Tag cannot start with `</` and end with `/>`") }
113 } else {
114 if !attrs.is_empty() {
115 quote! { compile_error!("End tag cannot have attribute") }
116 } else {
117 quote! {{
118 let mut event = ::quick_xml::events::BytesEnd::new(#name);
119 ::quick_xml::events::Event::End(event)
120 }}
121 }
122 }
123 } else {
124 let event_type = if self.self_close.is_some() {
125 quote! {Empty}
126 } else {
127 quote! {Start}
128 };
129 quote! {{
130 let mut event_start = ::quick_xml::events::BytesStart::new(#name);
131 #(#attrs)*
132 ::quick_xml::events::Event::#event_type(event_start)
133 }}
134 };
135
136 tokens.extend(expanded);
137 }
138}
139
140struct XmlId(Vec<ExtraXmlIdSeg>);
141
142impl XmlId {
143 pub fn to_lit_str(&self) -> LitStr {
144 let mut buf = String::new();
145 for x in &self.0 {
146 match x {
147 ExtraXmlIdSeg::Ident(x) => buf.push_str(&x.to_string()),
148 ExtraXmlIdSeg::Dot(_) => buf.push('.'),
149 ExtraXmlIdSeg::Colon(_) => buf.push(':'),
150 ExtraXmlIdSeg::Dash(_) => buf.push('-'),
151 }
152 }
153 LitStr::new(&buf, self.0[0].span())
154 }
155}
156
157impl Parse for XmlId {
158 fn parse(input: ParseStream) -> syn::Result<Self> {
159 let mut vec = Vec::new();
160 let mut last_one_ident = false;
161 loop {
162 if (input.peek(syn::Ident::peek_any) && !last_one_ident)
163 || input.peek(Token![.])
164 || input.peek(Token![:])
165 || input.peek(Token![-])
166 {
167 if input.peek(syn::Ident::peek_any) {
168 last_one_ident = true;
169 } else {
170 last_one_ident = false;
171 }
172 vec.push(input.parse()?);
173 } else {
174 break;
175 }
176 }
177 Ok(Self(vec))
178 }
179}
180
181impl ToTokens for XmlId {
182 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
183 for seg in &self.0 {
184 seg.to_tokens(tokens);
185 }
186 }
187}
188
189enum ExtraXmlIdSeg {
190 Ident(Ident),
191 Dot(Token![.]),
192 Colon(Token![:]),
193 Dash(Token![-]),
194}
195
196impl ExtraXmlIdSeg {
197 fn span(&self) -> proc_macro2::Span {
198 match self {
199 ExtraXmlIdSeg::Ident(x) => x.span(),
200 ExtraXmlIdSeg::Dot(x) => x.span(),
201 ExtraXmlIdSeg::Colon(x) => x.span(),
202 ExtraXmlIdSeg::Dash(x) => x.span(),
203 }
204 }
205}
206
207impl Parse for ExtraXmlIdSeg {
208 fn parse(input: ParseStream) -> syn::Result<Self> {
209 let lookahead = input.lookahead1();
210 if lookahead.peek(Token![.]) {
211 Ok(ExtraXmlIdSeg::Dot(input.parse()?))
212 } else if lookahead.peek(Token![:]) {
213 Ok(ExtraXmlIdSeg::Colon(input.parse()?))
214 } else if lookahead.peek(Token![-]) {
215 Ok(ExtraXmlIdSeg::Dash(input.parse()?))
216 } else {
217 Ok(ExtraXmlIdSeg::Ident(input.parse()?))
218 }
219 }
220}
221
222impl ToTokens for ExtraXmlIdSeg {
223 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
224 match self {
225 Self::Dot(x) => x.to_tokens(tokens),
226 Self::Colon(x) => x.to_tokens(tokens),
227 Self::Dash(x) => x.to_tokens(tokens),
228 Self::Ident(x) => x.to_tokens(tokens),
229 }
230 }
231}
232
233enum XmlAttribute {
235 Idented {
236 #[allow(dead_code)]
237 brace: Brace,
238 ident: Ident,
239 },
240 KV {
241 name: XmlId,
242 #[allow(dead_code)]
243 eq: Token![=],
244 value: XmlVal,
245 },
246}
247
248impl XmlAttribute {
249 fn key(&self) -> LitStr {
250 match self {
251 XmlAttribute::Idented { ident, .. } => LitStr::new(&ident.to_string(), ident.span()),
252 XmlAttribute::KV { name, .. } => name.to_lit_str(),
253 }
254 }
255
256 fn value(&self) -> TokenStream2 {
257 match self {
258 XmlAttribute::Idented { ident, .. } => quote!({&*#ident}),
259 XmlAttribute::KV { value, .. } => quote!(&*#value),
260 }
261 }
262}
263
264impl Parse for XmlAttribute {
265 fn parse(input: ParseStream) -> syn::Result<Self> {
266 let lookahead = input.lookahead1();
267 if lookahead.peek(syn::token::Brace) {
268 let content;
269 Ok(Self::Idented {
270 brace: braced!(content in input),
271 ident: content.parse()?,
272 })
273 } else {
274 Ok(Self::KV {
275 name: input.parse()?,
276 eq: input.parse()?,
277 value: input.parse()?,
278 })
279 }
280 }
281}
282
283enum XmlVal {
284 Str(LitStr),
285 Block(Block),
286}
287
288impl ToTokens for XmlVal {
289 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
290 match self {
291 Self::Str(x) => x.to_tokens(tokens),
292 Self::Block(x) => quote! {#x}.to_tokens(tokens),
293 }
294 }
295}
296
297impl Parse for XmlVal {
298 fn parse(input: ParseStream) -> syn::Result<Self> {
299 let lookahead = input.lookahead1();
300 if lookahead.peek(token::Brace) {
301 Ok(XmlVal::Block(input.parse()?))
302 } else {
303 Ok(XmlVal::Str(input.parse()?))
304 }
305 }
306}
307
308#[proc_macro]
309pub fn xml(input: TokenStream) -> TokenStream {
310 let input = parse_macro_input!(input as XmlEvent);
311 match input {
312 XmlEvent::Tag(tag) => tag.to_token_stream().into(),
313 XmlEvent::Text(text) => quote! {
314 ::quick_xml::events::Event::Text(
315 ::quick_xml::events::BytesText::new(&*#text)
316 )
317 }
318 .into(),
319 }
320}
321
322#[proc_macro]
323pub fn xmls(input: TokenStream) -> TokenStream {
324 let xmls = parse_macro_input!(input as Xmls);
325 let mut ts = TokenStream2::new();
326 for xml in xmls.events {
327 xml.to_tokens(&mut ts);
328 Token).to_tokens(&mut ts);
329 }
330 quote! {[#ts]}.into()
331}