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