1// Copyright (c) 2017-2018 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7macro_rules! get_attr {
8 ($elem:ident, $attr:tt, $type:tt) => (
9 get_attr!($elem, $attr, $type, value, value.parse()?)
10 );
11 ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => (
12 match $elem.attr($attr) {
13 Some("") => None,
14 Some($value) => Some($func),
15 None => None,
16 }
17 );
18 ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => (
19 match $elem.attr($attr) {
20 Some($value) => Some($func),
21 None => None,
22 }
23 );
24 ($elem:ident, $attr:tt, required, $value:ident, $func:expr) => (
25 match $elem.attr($attr) {
26 Some($value) => $func,
27 None => return Err(Error::ParseError(concat!("Required attribute '", $attr, "' missing."))),
28 }
29 );
30 ($elem:ident, $attr:tt, default, $value:ident, $func:expr) => (
31 match $elem.attr($attr) {
32 Some($value) => $func,
33 None => Default::default(),
34 }
35 );
36}
37
38macro_rules! generate_attribute {
39 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}) => (
40 generate_attribute!($elem, $name, {$($a => $b),+});
41 );
42 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+,}, Default = $default:ident) => (
43 generate_attribute!($elem, $name, {$($a => $b),+}, Default = $default);
44 );
45 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}) => (
46 #[derive(Debug, Clone, PartialEq)]
47 pub enum $elem {
48 $(
49 #[doc=$b]
50 #[doc="value for this attribute."]
51 $a
52 ),+
53 }
54 impl ::std::str::FromStr for $elem {
55 type Err = Error;
56 fn from_str(s: &str) -> Result<$elem, Error> {
57 Ok(match s {
58 $($b => $elem::$a),+,
59 _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
60 })
61 }
62 }
63 impl IntoAttributeValue for $elem {
64 fn into_attribute_value(self) -> Option<String> {
65 Some(String::from(match self {
66 $($elem::$a => $b),+
67 }))
68 }
69 }
70 );
71 ($elem:ident, $name:tt, {$($a:ident => $b:tt),+}, Default = $default:ident) => (
72 #[derive(Debug, Clone, PartialEq)]
73 pub enum $elem {
74 $(
75 #[doc=$b]
76 #[doc="value for this attribute."]
77 $a
78 ),+
79 }
80 impl ::std::str::FromStr for $elem {
81 type Err = Error;
82 fn from_str(s: &str) -> Result<$elem, Error> {
83 Ok(match s {
84 $($b => $elem::$a),+,
85 _ => return Err(Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
86 })
87 }
88 }
89 impl IntoAttributeValue for $elem {
90 #[allow(unreachable_patterns)]
91 fn into_attribute_value(self) -> Option<String> {
92 Some(String::from(match self {
93 $elem::$default => return None,
94 $($elem::$a => $b),+
95 }))
96 }
97 }
98 impl Default for $elem {
99 fn default() -> $elem {
100 $elem::$default
101 }
102 }
103 );
104}
105
106macro_rules! generate_element_enum {
107 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
108 generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
109 );
110 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
111 $(#[$meta])*
112 #[derive(Debug, Clone, PartialEq)]
113 pub enum $elem {
114 $(
115 $(#[$enum_meta])*
116 $enum
117 ),+
118 }
119 impl ::try_from::TryFrom<Element> for $elem {
120 type Err = Error;
121 fn try_from(elem: Element) -> Result<$elem, Error> {
122 check_ns_only!(elem, $name, $ns);
123 check_no_children!(elem, $name);
124 check_no_attributes!(elem, $name);
125 Ok(match elem.name() {
126 $($enum_name => $elem::$enum,)+
127 _ => return Err(Error::ParseError(concat!("This is not a ", $name, " element."))),
128 })
129 }
130 }
131 impl From<$elem> for Element {
132 fn from(elem: $elem) -> Element {
133 Element::builder(match elem {
134 $($elem::$enum => $enum_name,)+
135 }).ns($ns)
136 .build()
137 }
138 }
139 );
140}
141
142macro_rules! generate_attribute_enum {
143 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
144 generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
145 );
146 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
147 $(#[$meta])*
148 #[derive(Debug, Clone, PartialEq)]
149 pub enum $elem {
150 $(
151 $(#[$enum_meta])*
152 $enum
153 ),+
154 }
155 impl ::try_from::TryFrom<Element> for $elem {
156 type Err = Error;
157 fn try_from(elem: Element) -> Result<$elem, Error> {
158 check_ns_only!(elem, $name, $ns);
159 check_no_children!(elem, $name);
160 check_no_unknown_attributes!(elem, $name, [$attr]);
161 Ok(match get_attr!(elem, $attr, required) {
162 $($enum_name => $elem::$enum,)+
163 _ => return Err(Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
164 })
165 }
166 }
167 impl From<$elem> for Element {
168 fn from(elem: $elem) -> Element {
169 Element::builder($name)
170 .ns($ns)
171 .attr($attr, match elem {
172 $($elem::$enum => $enum_name,)+
173 })
174 .build()
175 }
176 }
177 );
178}
179
180macro_rules! check_self {
181 ($elem:ident, $name:tt, $ns:expr) => (
182 check_self!($elem, $name, $ns, $name);
183 );
184 ($elem:ident, $name:tt, $ns:expr, $pretty_name:tt) => (
185 if !$elem.is($name, $ns) {
186 return Err(Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
187 }
188 );
189}
190
191macro_rules! check_ns_only {
192 ($elem:ident, $name:tt, $ns:expr) => (
193 if !$elem.has_ns($ns) {
194 return Err(Error::ParseError(concat!("This is not a ", $name, " element.")));
195 }
196 );
197}
198
199macro_rules! check_no_children {
200 ($elem:ident, $name:tt) => (
201 for _ in $elem.children() {
202 return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
203 }
204 );
205}
206
207macro_rules! check_no_attributes {
208 ($elem:ident, $name:tt) => (
209 for _ in $elem.attrs() {
210 return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
211 }
212 );
213}
214
215macro_rules! check_no_unknown_attributes {
216 ($elem:ident, $name:tt, [$($attr:tt),*]) => (
217 for (_attr, _) in $elem.attrs() {
218 $(
219 if _attr == $attr {
220 continue;
221 }
222 )*
223 return Err(Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
224 }
225 );
226}
227
228macro_rules! generate_empty_element {
229 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr) => (
230 $(#[$meta])*
231 #[derive(Debug, Clone)]
232 pub struct $elem;
233
234 impl ::try_from::TryFrom<Element> for $elem {
235 type Err = Error;
236
237 fn try_from(elem: Element) -> Result<$elem, Error> {
238 check_self!(elem, $name, $ns);
239 check_no_children!(elem, $name);
240 check_no_attributes!(elem, $name);
241 Ok($elem)
242 }
243 }
244
245 impl From<$elem> for Element {
246 fn from(_: $elem) -> Element {
247 Element::builder($name)
248 .ns($ns)
249 .build()
250 }
251 }
252 );
253}
254
255macro_rules! generate_element_with_only_attributes {
256 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
257 generate_element_with_only_attributes!($(#[$meta])* $elem, $name, $ns, [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*]);
258 );
259 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
260 $(#[$meta])*
261 #[derive(Debug, Clone)]
262 pub struct $elem {
263 $(
264 $(#[$attr_meta])*
265 pub $attr: $attr_type,
266 )*
267 }
268
269 impl ::try_from::TryFrom<Element> for $elem {
270 type Err = Error;
271
272 fn try_from(elem: Element) -> Result<$elem, Error> {
273 check_self!(elem, $name, $ns);
274 check_no_children!(elem, $name);
275 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
276 Ok($elem {
277 $(
278 $attr: get_attr!(elem, $attr_name, $attr_action),
279 )*
280 })
281 }
282 }
283
284 impl From<$elem> for Element {
285 fn from(elem: $elem) -> Element {
286 Element::builder($name)
287 .ns($ns)
288 $(
289 .attr($attr_name, elem.$attr)
290 )*
291 .build()
292 }
293 }
294 );
295}
296
297macro_rules! generate_id {
298 ($elem:ident) => (
299 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
300 pub struct $elem(pub String);
301 impl ::std::str::FromStr for $elem {
302 type Err = Error;
303 fn from_str(s: &str) -> Result<$elem, Error> {
304 // TODO: add a way to parse that differently when needed.
305 Ok($elem(String::from(s)))
306 }
307 }
308 impl IntoAttributeValue for $elem {
309 fn into_attribute_value(self) -> Option<String> {
310 Some(self.0)
311 }
312 }
313 );
314}
315
316macro_rules! generate_elem_id {
317 ($elem:ident, $name:tt, $ns:expr) => (
318 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
319 pub struct $elem(pub String);
320 impl ::std::str::FromStr for $elem {
321 type Err = Error;
322 fn from_str(s: &str) -> Result<$elem, Error> {
323 // TODO: add a way to parse that differently when needed.
324 Ok($elem(String::from(s)))
325 }
326 }
327 impl ::try_from::TryFrom<Element> for $elem {
328 type Err = Error;
329 fn try_from(elem: Element) -> Result<$elem, Error> {
330 check_self!(elem, $name, $ns);
331 check_no_children!(elem, $name);
332 check_no_attributes!(elem, $name);
333 // TODO: add a way to parse that differently when needed.
334 Ok($elem(elem.text()))
335 }
336 }
337 impl From<$elem> for Element {
338 fn from(elem: $elem) -> Element {
339 Element::builder($name)
340 .ns($ns)
341 .append(elem.0)
342 .build()
343 }
344 }
345 );
346}
347
348macro_rules! generate_element_with_text {
349 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, $text_ident:ident: $codec:ident < $text_type:ty >) => (
350 generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $text_ident: $codec<$text_type>);
351 );
352 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $text_ident:ident: $codec:ident < $text_type:ty >) => (
353 $(#[$meta])*
354 #[derive(Debug, Clone)]
355 pub struct $elem {
356 $(
357 $(#[$attr_meta])*
358 pub $attr: $attr_type,
359 )*
360 pub $text_ident: $text_type,
361 }
362
363 impl ::try_from::TryFrom<Element> for $elem {
364 type Err = Error;
365
366 fn try_from(elem: Element) -> Result<$elem, Error> {
367 check_self!(elem, $name, $ns);
368 check_no_children!(elem, $name);
369 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
370 Ok($elem {
371 $(
372 $attr: get_attr!(elem, $attr_name, $attr_action),
373 )*
374 $text_ident: $codec::decode(&elem.text())?,
375 })
376 }
377 }
378
379 impl From<$elem> for Element {
380 fn from(elem: $elem) -> Element {
381 Element::builder($name)
382 .ns($ns)
383 $(
384 .attr($attr_name, elem.$attr)
385 )*
386 .append($codec::encode(&elem.$text_ident))
387 .build()
388 }
389 }
390 );
391}
392
393macro_rules! generate_element_with_children {
394 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:expr, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:expr) => $child_constructor:ident),+]) => (
395 $(#[$meta])*
396 #[derive(Debug, Clone)]
397 pub struct $elem {
398 $(
399 $(#[$attr_meta])*
400 pub $attr: $attr_type,
401 )*
402 $(
403 $(#[$child_meta])*
404 pub $child_ident: Vec<$child_type>,
405 )*
406 }
407
408 impl ::try_from::TryFrom<Element> for $elem {
409 type Err = Error;
410
411 fn try_from(elem: Element) -> Result<$elem, Error> {
412 check_self!(elem, $name, $ns);
413 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
414 let mut parsed_children = vec!();
415 for child in elem.children() {
416 $(
417 if child.is($child_name, $child_ns) {
418 let parsed_child = $child_constructor::try_from(child.clone())?;
419 parsed_children.push(parsed_child);
420 continue;
421 }
422 )*
423 return Err(Error::ParseError(concat!("Unknown child in ", $name, " element.")));
424 }
425 Ok($elem {
426 $(
427 $attr: get_attr!(elem, $attr_name, $attr_action),
428 )*
429 $(
430 $child_ident: parsed_children,
431 )*
432 })
433 }
434 }
435
436 impl From<$elem> for Element {
437 fn from(elem: $elem) -> Element {
438 Element::builder($name)
439 .ns($ns)
440 $(
441 .attr($attr_name, elem.$attr)
442 )*
443 $(
444 .append(elem.$child_ident)
445 )*
446 .build()
447 }
448 }
449 );
450}