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::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 => ::std::default::Default::default(),
34 }
35 );
36}
37
38macro_rules! generate_attribute {
39 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => (
40 generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+});
41 );
42 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => (
43 generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default);
44 );
45 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}) => (
46 $(#[$meta])*
47 #[derive(Debug, Clone, PartialEq)]
48 pub enum $elem {
49 $(
50 $(#[$a_meta])*
51 $a
52 ),+
53 }
54 impl ::std::str::FromStr for $elem {
55 type Err = ::error::Error;
56 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
57 Ok(match s {
58 $($b => $elem::$a),+,
59 _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
60 })
61 }
62 }
63 impl ::minidom::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 ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}, Default = $default:ident) => (
72 $(#[$meta])*
73 #[derive(Debug, Clone, PartialEq)]
74 pub enum $elem {
75 $(
76 $(#[$a_meta])*
77 $a
78 ),+
79 }
80 impl ::std::str::FromStr for $elem {
81 type Err = ::error::Error;
82 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
83 Ok(match s {
84 $($b => $elem::$a),+,
85 _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
86 })
87 }
88 }
89 impl ::minidom::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 ::std::default::Default for $elem {
99 fn default() -> $elem {
100 $elem::$default
101 }
102 }
103 );
104 ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => (
105 $(#[$meta])*
106 #[derive(Debug, Clone, PartialEq)]
107 pub enum $elem {
108 /// True value, represented by either 'true' or '1'.
109 True,
110 /// False value, represented by either 'false' or '0'.
111 False,
112 }
113 impl ::std::str::FromStr for $elem {
114 type Err = ::error::Error;
115 fn from_str(s: &str) -> Result<Self, ::error::Error> {
116 Ok(match s {
117 "true" | "1" => $elem::True,
118 "false" | "0" => $elem::False,
119 _ => return Err(::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
120 })
121 }
122 }
123 impl ::minidom::IntoAttributeValue for $elem {
124 fn into_attribute_value(self) -> Option<String> {
125 match self {
126 $elem::True => Some(String::from("true")),
127 $elem::False => None
128 }
129 }
130 }
131 impl ::std::default::Default for $elem {
132 fn default() -> $elem {
133 $elem::False
134 }
135 }
136 );
137}
138
139macro_rules! generate_element_enum {
140 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
141 generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
142 );
143 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
144 $(#[$meta])*
145 #[derive(Debug, Clone, PartialEq)]
146 pub enum $elem {
147 $(
148 $(#[$enum_meta])*
149 $enum
150 ),+
151 }
152 impl ::try_from::TryFrom<::minidom::Element> for $elem {
153 type Err = ::error::Error;
154 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
155 check_ns_only!(elem, $name, $ns);
156 check_no_children!(elem, $name);
157 check_no_attributes!(elem, $name);
158 Ok(match elem.name() {
159 $($enum_name => $elem::$enum,)+
160 _ => return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element."))),
161 })
162 }
163 }
164 impl From<$elem> for ::minidom::Element {
165 fn from(elem: $elem) -> ::minidom::Element {
166 ::minidom::Element::builder(match elem {
167 $($elem::$enum => $enum_name,)+
168 }).ns(::ns::$ns)
169 .build()
170 }
171 }
172 );
173}
174
175macro_rules! generate_attribute_enum {
176 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
177 generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
178 );
179 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
180 $(#[$meta])*
181 #[derive(Debug, Clone, PartialEq)]
182 pub enum $elem {
183 $(
184 $(#[$enum_meta])*
185 $enum
186 ),+
187 }
188 impl ::try_from::TryFrom<::minidom::Element> for $elem {
189 type Err = ::error::Error;
190 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
191 check_ns_only!(elem, $name, $ns);
192 check_no_children!(elem, $name);
193 check_no_unknown_attributes!(elem, $name, [$attr]);
194 Ok(match get_attr!(elem, $attr, required) {
195 $($enum_name => $elem::$enum,)+
196 _ => return Err(::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
197 })
198 }
199 }
200 impl From<$elem> for ::minidom::Element {
201 fn from(elem: $elem) -> ::minidom::Element {
202 ::minidom::Element::builder($name)
203 .ns(::ns::$ns)
204 .attr($attr, match elem {
205 $($elem::$enum => $enum_name,)+
206 })
207 .build()
208 }
209 }
210 );
211}
212
213macro_rules! check_self {
214 ($elem:ident, $name:tt, $ns:ident) => (
215 check_self!($elem, $name, $ns, $name);
216 );
217 ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => (
218 if !$elem.is($name, ::ns::$ns) {
219 return Err(::error::Error::ParseError(concat!("This is not a ", $pretty_name, " element.")));
220 }
221 );
222}
223
224macro_rules! check_ns_only {
225 ($elem:ident, $name:tt, $ns:ident) => (
226 if !$elem.has_ns(::ns::$ns) {
227 return Err(::error::Error::ParseError(concat!("This is not a ", $name, " element.")));
228 }
229 );
230}
231
232macro_rules! check_no_children {
233 ($elem:ident, $name:tt) => (
234 for _ in $elem.children() {
235 return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
236 }
237 );
238}
239
240macro_rules! check_no_attributes {
241 ($elem:ident, $name:tt) => (
242 for _ in $elem.attrs() {
243 return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
244 }
245 );
246}
247
248macro_rules! check_no_unknown_attributes {
249 ($elem:ident, $name:tt, [$($attr:tt),*]) => (
250 for (_attr, _) in $elem.attrs() {
251 $(
252 if _attr == $attr {
253 continue;
254 }
255 )*
256 return Err(::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
257 }
258 );
259}
260
261macro_rules! generate_empty_element {
262 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
263 $(#[$meta])*
264 #[derive(Debug, Clone)]
265 pub struct $elem;
266
267 impl ::try_from::TryFrom<::minidom::Element> for $elem {
268 type Err = ::error::Error;
269
270 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
271 check_self!(elem, $name, $ns);
272 check_no_children!(elem, $name);
273 check_no_attributes!(elem, $name);
274 Ok($elem)
275 }
276 }
277
278 impl From<$elem> for ::minidom::Element {
279 fn from(_: $elem) -> ::minidom::Element {
280 ::minidom::Element::builder($name)
281 .ns(::ns::$ns)
282 .build()
283 }
284 }
285 );
286}
287
288macro_rules! generate_id {
289 ($(#[$meta:meta])* $elem:ident) => (
290 $(#[$meta])*
291 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
292 pub struct $elem(pub String);
293 impl ::std::str::FromStr for $elem {
294 type Err = ::error::Error;
295 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
296 // TODO: add a way to parse that differently when needed.
297 Ok($elem(String::from(s)))
298 }
299 }
300 impl ::minidom::IntoAttributeValue for $elem {
301 fn into_attribute_value(self) -> Option<String> {
302 Some(self.0)
303 }
304 }
305 );
306}
307
308macro_rules! generate_elem_id {
309 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
310 $(#[$meta])*
311 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
312 pub struct $elem(pub String);
313 impl ::std::str::FromStr for $elem {
314 type Err = ::error::Error;
315 fn from_str(s: &str) -> Result<$elem, ::error::Error> {
316 // TODO: add a way to parse that differently when needed.
317 Ok($elem(String::from(s)))
318 }
319 }
320 impl ::try_from::TryFrom<::minidom::Element> for $elem {
321 type Err = ::error::Error;
322 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
323 check_self!(elem, $name, $ns);
324 check_no_children!(elem, $name);
325 check_no_attributes!(elem, $name);
326 // TODO: add a way to parse that differently when needed.
327 Ok($elem(elem.text()))
328 }
329 }
330 impl From<$elem> for ::minidom::Element {
331 fn from(elem: $elem) -> ::minidom::Element {
332 ::minidom::Element::builder($name)
333 .ns(::ns::$ns)
334 .append(elem.0)
335 .build()
336 }
337 }
338 );
339}
340
341macro_rules! generate_element_with_text {
342 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >) => (
343 generate_element_with_text!($(#[$meta])* $elem, $name, $ns, [], $(#[$text_meta])* $text_ident: $codec<$text_type>);
344 );
345 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], $(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >) => (
346 $(#[$meta])*
347 #[derive(Debug, Clone)]
348 pub struct $elem {
349 $(
350 $(#[$attr_meta])*
351 pub $attr: $attr_type,
352 )*
353 $(#[$text_meta])*
354 pub $text_ident: $text_type,
355 }
356
357 impl ::try_from::TryFrom<::minidom::Element> for $elem {
358 type Err = ::error::Error;
359
360 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
361 check_self!(elem, $name, $ns);
362 check_no_children!(elem, $name);
363 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
364 Ok($elem {
365 $(
366 $attr: get_attr!(elem, $attr_name, $attr_action),
367 )*
368 $text_ident: $codec::decode(&elem.text())?,
369 })
370 }
371 }
372
373 impl From<$elem> for ::minidom::Element {
374 fn from(elem: $elem) -> ::minidom::Element {
375 ::minidom::Element::builder($name)
376 .ns(::ns::$ns)
377 $(
378 .attr($attr_name, elem.$attr)
379 )*
380 .append($codec::encode(&elem.$text_ident))
381 .build()
382 }
383 }
384 );
385}
386
387macro_rules! start_decl {
388 (Vec, $type:ty) => (
389 Vec<$type>
390 );
391 (Option, $type:ty) => (
392 Option<$type>
393 );
394 (Required, $type:ty) => (
395 $type
396 );
397}
398
399macro_rules! start_parse_elem {
400 ($temp:ident: Vec) => (
401 let mut $temp = Vec::new();
402 );
403 ($temp:ident: Option) => (
404 let mut $temp = None;
405 );
406 ($temp:ident: Required) => (
407 let mut $temp = None;
408 );
409}
410
411macro_rules! do_parse {
412 ($elem:ident, Element) => (
413 $elem.clone()
414 );
415 ($elem:ident, String) => (
416 $elem.text()
417 );
418 ($elem:ident, $constructor:ident) => (
419 $constructor::try_from($elem.clone())?
420 );
421}
422
423macro_rules! do_parse_elem {
424 ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
425 $temp.push(do_parse!($elem, $constructor));
426 );
427 ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
428 if $temp.is_some() {
429 return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
430 }
431 $temp = Some(do_parse!($elem, $constructor));
432 );
433 ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => (
434 if $temp.is_some() {
435 return Err(::error::Error::ParseError(concat!("Element ", $parent_name, " must not have more than one ", $name, " child.")));
436 }
437 $temp = Some(do_parse!($elem, $constructor));
438 );
439}
440
441macro_rules! finish_parse_elem {
442 ($temp:ident: Vec = $name:tt, $parent_name:tt) => (
443 $temp
444 );
445 ($temp:ident: Option = $name:tt, $parent_name:tt) => (
446 $temp
447 );
448 ($temp:ident: Required = $name:tt, $parent_name:tt) => (
449 $temp.ok_or(::error::Error::ParseError(concat!("Missing child ", $name, " in ", $parent_name, " element.")))?
450 );
451}
452
453macro_rules! generate_serialiser {
454 ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => (
455 ::minidom::Element::builder($name)
456 .ns(::ns::$ns)
457 .append($parent.$elem)
458 .build()
459 );
460 ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
461 $parent.$elem.map(|elem|
462 ::minidom::Element::builder($name)
463 .ns(::ns::$ns)
464 .append(elem)
465 .build())
466 );
467 ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
468 $parent.$elem
469 );
470}
471
472macro_rules! generate_element {
473 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+,]) => (
474 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []);
475 );
476 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),+]) => (
477 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: []);
478 );
479 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
480 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
481 );
482 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
483 generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
484 );
485 ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),*]) => (
486 $(#[$meta])*
487 #[derive(Debug, Clone)]
488 pub struct $elem {
489 $(
490 $(#[$attr_meta])*
491 pub $attr: $attr_type,
492 )*
493 $(
494 $(#[$child_meta])*
495 pub $child_ident: start_decl!($coucou, $child_type),
496 )*
497 }
498
499 impl ::try_from::TryFrom<::minidom::Element> for $elem {
500 type Err = ::error::Error;
501
502 fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
503 check_self!(elem, $name, $ns);
504 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
505 $(
506 start_parse_elem!($child_ident: $coucou);
507 )*
508 for _child in elem.children() {
509 $(
510 if _child.is($child_name, ::ns::$child_ns) {
511 do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
512 continue;
513 }
514 )*
515 return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
516 }
517 Ok($elem {
518 $(
519 $attr: get_attr!(elem, $attr_name, $attr_action),
520 )*
521 $(
522 $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
523 )*
524 })
525 }
526 }
527
528 impl From<$elem> for ::minidom::Element {
529 fn from(elem: $elem) -> ::minidom::Element {
530 ::minidom::Element::builder($name)
531 .ns(::ns::$ns)
532 $(
533 .attr($attr_name, elem.$attr)
534 )*
535 $(
536 .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns)))
537 )*
538 .build()
539 }
540 }
541 );
542}