macros.rs

  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, OptionEmpty, $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, Option, $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, RequiredNonEmpty, $value:ident, $func:expr) => {
 37        match $elem.attr($attr) {
 38            Some("") => {
 39                return Err(crate::util::error::Error::ParseError(concat!(
 40                    "Required attribute '",
 41                    $attr,
 42                    "' must not be empty."
 43                )));
 44            }
 45            Some($value) => $func,
 46            None => {
 47                return Err(crate::util::error::Error::ParseError(concat!(
 48                    "Required attribute '",
 49                    $attr,
 50                    "' missing."
 51                )));
 52            }
 53        }
 54    };
 55    ($elem:ident, $attr:tt, Default, $value:ident, $func:expr) => {
 56        match $elem.attr($attr) {
 57            Some($value) => $func,
 58            None => ::std::default::Default::default(),
 59        }
 60    };
 61}
 62
 63macro_rules! generate_attribute {
 64    ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}) => (
 65        generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+});
 66    );
 67    ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+,}, Default = $default:ident) => (
 68        generate_attribute!($(#[$meta])* $elem, $name, {$($(#[$a_meta])* $a => $b),+}, Default = $default);
 69    );
 70    ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}) => (
 71        $(#[$meta])*
 72        #[derive(Debug, Clone, PartialEq)]
 73        pub enum $elem {
 74            $(
 75                $(#[$a_meta])*
 76                $a
 77            ),+
 78        }
 79        impl ::std::str::FromStr for $elem {
 80            type Err = crate::util::error::Error;
 81            fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
 82                Ok(match s {
 83                    $($b => $elem::$a),+,
 84                    _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
 85                })
 86            }
 87        }
 88        impl std::fmt::Display for $elem {
 89            fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
 90                write!(fmt, "{}", match self {
 91                    $($elem::$a => $b),+
 92                })
 93            }
 94        }
 95        impl ::minidom::IntoAttributeValue for $elem {
 96            fn into_attribute_value(self) -> Option<String> {
 97                Some(String::from(match self {
 98                    $($elem::$a => $b),+
 99                }))
100            }
101        }
102    );
103    ($(#[$meta:meta])* $elem:ident, $name:tt, {$($(#[$a_meta:meta])* $a:ident => $b:tt),+}, Default = $default:ident) => (
104        $(#[$meta])*
105        #[derive(Debug, Clone, PartialEq)]
106        pub enum $elem {
107            $(
108                $(#[$a_meta])*
109                $a
110            ),+
111        }
112        impl ::std::str::FromStr for $elem {
113            type Err = crate::util::error::Error;
114            fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
115                Ok(match s {
116                    $($b => $elem::$a),+,
117                    _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
118                })
119            }
120        }
121        impl ::minidom::IntoAttributeValue for $elem {
122            #[allow(unreachable_patterns)]
123            fn into_attribute_value(self) -> Option<String> {
124                Some(String::from(match self {
125                    $elem::$default => return None,
126                    $($elem::$a => $b),+
127                }))
128            }
129        }
130        impl ::std::default::Default for $elem {
131            fn default() -> $elem {
132                $elem::$default
133            }
134        }
135    );
136    ($(#[$meta:meta])* $elem:ident, $name:tt, ($(#[$meta_symbol:meta])* $symbol:ident => $value:tt)) => (
137        $(#[$meta])*
138        #[derive(Debug, Clone, PartialEq)]
139        pub enum $elem {
140            $(#[$meta_symbol])*
141            $symbol,
142            /// Value when absent.
143            None,
144        }
145        impl ::std::str::FromStr for $elem {
146            type Err = crate::util::error::Error;
147            fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
148                Ok(match s {
149                    $value => $elem::$symbol,
150                    _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
151                })
152            }
153        }
154        impl ::minidom::IntoAttributeValue for $elem {
155            fn into_attribute_value(self) -> Option<String> {
156                match self {
157                    $elem::$symbol => Some(String::from($value)),
158                    $elem::None => None
159                }
160            }
161        }
162        impl ::std::default::Default for $elem {
163            fn default() -> $elem {
164                $elem::None
165            }
166        }
167    );
168    ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => (
169        $(#[$meta])*
170        #[derive(Debug, Clone, PartialEq)]
171        pub enum $elem {
172            /// True value, represented by either 'true' or '1'.
173            True,
174            /// False value, represented by either 'false' or '0'.
175            False,
176        }
177        impl ::std::str::FromStr for $elem {
178            type Err = crate::util::error::Error;
179            fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
180                Ok(match s {
181                    "true" | "1" => $elem::True,
182                    "false" | "0" => $elem::False,
183                    _ => return Err(crate::util::error::Error::ParseError(concat!("Unknown value for '", $name, "' attribute."))),
184                })
185            }
186        }
187        impl ::minidom::IntoAttributeValue for $elem {
188            fn into_attribute_value(self) -> Option<String> {
189                match self {
190                    $elem::True => Some(String::from("true")),
191                    $elem::False => None
192                }
193            }
194        }
195        impl ::std::default::Default for $elem {
196            fn default() -> $elem {
197                $elem::False
198            }
199        }
200    );
201    ($(#[$meta:meta])* $elem:ident, $name:tt, $type:tt, Default = $default:expr) => (
202        $(#[$meta])*
203        #[derive(Debug, Clone, PartialEq)]
204        pub struct $elem(pub $type);
205        impl ::std::str::FromStr for $elem {
206            type Err = crate::util::error::Error;
207            fn from_str(s: &str) -> Result<Self, crate::util::error::Error> {
208                Ok($elem($type::from_str(s)?))
209            }
210        }
211        impl ::minidom::IntoAttributeValue for $elem {
212            fn into_attribute_value(self) -> Option<String> {
213                match self {
214                    $elem($default) => None,
215                    $elem(value) => Some(format!("{}", value)),
216                }
217            }
218        }
219        impl ::std::default::Default for $elem {
220            fn default() -> $elem {
221                $elem($default)
222            }
223        }
224    );
225}
226
227macro_rules! generate_element_enum {
228    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
229        generate_element_enum!($(#[$meta])* $elem, $name, $ns, {$($(#[$enum_meta])* $enum => $enum_name),+});
230    );
231    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
232        $(#[$meta])*
233        #[derive(Debug, Clone, PartialEq)]
234        pub enum $elem {
235            $(
236                $(#[$enum_meta])*
237                $enum
238            ),+
239        }
240        impl ::std::convert::TryFrom<crate::Element> for $elem {
241            type Error = crate::util::error::Error;
242            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
243                check_ns_only!(elem, $name, $ns);
244                check_no_children!(elem, $name);
245                check_no_attributes!(elem, $name);
246                Ok(match elem.name() {
247                    $($enum_name => $elem::$enum,)+
248                    _ => return Err(crate::util::error::Error::ParseError(concat!("This is not a ", $name, " element."))),
249                })
250            }
251        }
252        impl From<$elem> for crate::Element {
253            fn from(elem: $elem) -> crate::Element {
254                crate::Element::builder(
255                    match elem {
256                        $($elem::$enum => $enum_name,)+
257                    },
258                    crate::ns::$ns,
259                )
260                    .build()
261            }
262        }
263    );
264}
265
266macro_rules! generate_attribute_enum {
267    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
268        generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
269    );
270    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
271        $(#[$meta])*
272        #[derive(Debug, Clone, PartialEq)]
273        pub enum $elem {
274            $(
275                $(#[$enum_meta])*
276                $enum
277            ),+
278        }
279        impl ::std::convert::TryFrom<crate::Element> for $elem {
280            type Error = crate::util::error::Error;
281            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
282                check_ns_only!(elem, $name, $ns);
283                check_no_children!(elem, $name);
284                check_no_unknown_attributes!(elem, $name, [$attr]);
285                Ok(match get_attr!(elem, $attr, Required) {
286                    $($enum_name => $elem::$enum,)+
287                    _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
288                })
289            }
290        }
291        impl From<$elem> for crate::Element {
292            fn from(elem: $elem) -> crate::Element {
293                crate::Element::builder($name, crate::ns::$ns)
294                    .attr($attr, match elem {
295                         $($elem::$enum => $enum_name,)+
296                     })
297                     .build()
298            }
299        }
300    );
301}
302
303macro_rules! check_self {
304    ($elem:ident, $name:tt, $ns:ident) => {
305        check_self!($elem, $name, $ns, $name);
306    };
307    ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => {
308        if !$elem.is($name, crate::ns::$ns) {
309            return Err(crate::util::error::Error::ParseError(concat!(
310                "This is not a ",
311                $pretty_name,
312                " element."
313            )));
314        }
315    };
316}
317
318macro_rules! check_ns_only {
319    ($elem:ident, $name:tt, $ns:ident) => {
320        if !$elem.has_ns(crate::ns::$ns) {
321            return Err(crate::util::error::Error::ParseError(concat!(
322                "This is not a ",
323                $name,
324                " element."
325            )));
326        }
327    };
328}
329
330macro_rules! check_no_children {
331    ($elem:ident, $name:tt) => {
332        #[cfg(not(feature = "disable-validation"))]
333        for _ in $elem.children() {
334            return Err(crate::util::error::Error::ParseError(concat!(
335                "Unknown child in ",
336                $name,
337                " element."
338            )));
339        }
340    };
341}
342
343macro_rules! check_no_attributes {
344    ($elem:ident, $name:tt) => {
345        #[cfg(not(feature = "disable-validation"))]
346        for _ in $elem.attrs() {
347            return Err(crate::util::error::Error::ParseError(concat!(
348                "Unknown attribute in ",
349                $name,
350                " element."
351            )));
352        }
353    };
354}
355
356macro_rules! check_no_unknown_attributes {
357    ($elem:ident, $name:tt, [$($attr:tt),*]) => (
358        #[cfg(not(feature = "disable-validation"))]
359        for (_attr, _) in $elem.attrs() {
360            $(
361                if _attr == $attr {
362                    continue;
363                }
364            )*
365            return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
366        }
367    );
368}
369
370macro_rules! generate_empty_element {
371    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
372        $(#[$meta])*
373        #[derive(Debug, Clone, PartialEq)]
374        pub struct $elem;
375
376        impl ::std::convert::TryFrom<crate::Element> for $elem {
377            type Error = crate::util::error::Error;
378
379            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
380                check_self!(elem, $name, $ns);
381                check_no_children!(elem, $name);
382                check_no_attributes!(elem, $name);
383                Ok($elem)
384            }
385        }
386
387        impl From<$elem> for crate::Element {
388            fn from(_: $elem) -> crate::Element {
389                crate::Element::builder($name, crate::ns::$ns)
390                    .build()
391            }
392        }
393    );
394}
395
396macro_rules! generate_id {
397    ($(#[$meta:meta])* $elem:ident) => (
398        $(#[$meta])*
399        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
400        pub struct $elem(pub String);
401        impl ::std::str::FromStr for $elem {
402            type Err = crate::util::error::Error;
403            fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
404                // TODO: add a way to parse that differently when needed.
405                Ok($elem(String::from(s)))
406            }
407        }
408        impl ::minidom::IntoAttributeValue for $elem {
409            fn into_attribute_value(self) -> Option<String> {
410                Some(self.0)
411            }
412        }
413    );
414}
415
416macro_rules! generate_elem_id {
417    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
418        generate_elem_id!($(#[$meta])* $elem, $name, $ns, String);
419        impl ::std::str::FromStr for $elem {
420            type Err = crate::util::error::Error;
421            fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
422                // TODO: add a way to parse that differently when needed.
423                Ok($elem(String::from(s)))
424            }
425        }
426    );
427    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $type:ty) => (
428        $(#[$meta])*
429        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
430        pub struct $elem(pub $type);
431        impl ::std::convert::TryFrom<crate::Element> for $elem {
432            type Error = crate::util::error::Error;
433            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
434                check_self!(elem, $name, $ns);
435                check_no_children!(elem, $name);
436                check_no_attributes!(elem, $name);
437                // TODO: add a way to parse that differently when needed.
438                Ok($elem(elem.text().parse()?))
439            }
440        }
441        impl From<$elem> for crate::Element {
442            fn from(elem: $elem) -> crate::Element {
443                crate::Element::builder($name, crate::ns::$ns)
444                    .append(elem.0.to_string())
445                    .build()
446            }
447        }
448    );
449}
450
451macro_rules! decl_attr {
452    (OptionEmpty, $type:ty) => (
453        Option<$type>
454    );
455    (Option, $type:ty) => (
456        Option<$type>
457    );
458    (Required, $type:ty) => (
459        $type
460    );
461    (RequiredNonEmpty, $type:ty) => (
462        $type
463    );
464    (Default, $type:ty) => (
465        $type
466    );
467}
468
469macro_rules! start_decl {
470    (Vec, $type:ty) => (
471        Vec<$type>
472    );
473    (Option, $type:ty) => (
474        Option<$type>
475    );
476    (Required, $type:ty) => (
477        $type
478    );
479    (Present, $type:ty) => (
480        bool
481    );
482}
483
484macro_rules! start_parse_elem {
485    ($temp:ident: Vec) => {
486        let mut $temp = Vec::new();
487    };
488    ($temp:ident: Option) => {
489        let mut $temp = None;
490    };
491    ($temp:ident: Required) => {
492        let mut $temp = None;
493    };
494    ($temp:ident: Present) => {
495        let mut $temp = false;
496    };
497}
498
499macro_rules! do_parse {
500    ($elem:ident, Element) => {
501        $elem.clone()
502    };
503    ($elem:ident, String) => {
504        $elem.text()
505    };
506    ($elem:ident, $constructor:ident) => {
507        $constructor::try_from($elem.clone())?
508    };
509}
510
511macro_rules! do_parse_elem {
512    ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
513        $temp.push(do_parse!($elem, $constructor));
514    };
515    ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
516        if $temp.is_some() {
517            return Err(crate::util::error::Error::ParseError(concat!(
518                "Element ",
519                $parent_name,
520                " must not have more than one ",
521                $name,
522                " child."
523            )));
524        }
525        $temp = Some(do_parse!($elem, $constructor));
526    };
527    ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
528        if $temp.is_some() {
529            return Err(crate::util::error::Error::ParseError(concat!(
530                "Element ",
531                $parent_name,
532                " must not have more than one ",
533                $name,
534                " child."
535            )));
536        }
537        $temp = Some(do_parse!($elem, $constructor));
538    };
539    ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
540        if $temp {
541            return Err(crate::util::error::Error::ParseError(concat!(
542                "Element ",
543                $parent_name,
544                " must not have more than one ",
545                $name,
546                " child."
547            )));
548        }
549        $temp = true;
550    };
551}
552
553macro_rules! finish_parse_elem {
554    ($temp:ident: Vec = $name:tt, $parent_name:tt) => {
555        $temp
556    };
557    ($temp:ident: Option = $name:tt, $parent_name:tt) => {
558        $temp
559    };
560    ($temp:ident: Required = $name:tt, $parent_name:tt) => {
561        $temp.ok_or(crate::util::error::Error::ParseError(concat!(
562            "Missing child ",
563            $name,
564            " in ",
565            $parent_name,
566            " element."
567        )))?
568    };
569    ($temp:ident: Present = $name:tt, $parent_name:tt) => {
570        $temp
571    };
572}
573
574macro_rules! generate_serialiser {
575    ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
576        $builder.append(
577            crate::Element::builder($name, crate::ns::$ns)
578                .append(::minidom::Node::Text($parent.$elem)),
579        )
580    };
581    ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
582        $builder.append_all($parent.$elem.map(|elem| {
583            crate::Element::builder($name, crate::ns::$ns).append(::minidom::Node::Text(elem))
584        }))
585    };
586    ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, *)) => {
587        $builder.append_all(
588            $parent
589                .$elem
590                .map(|elem| ::minidom::Node::Element(crate::Element::from(elem))),
591        )
592    };
593    ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => {
594        $builder.append_all(
595            $parent
596                .$elem
597                .map(|elem| ::minidom::Node::Element(crate::Element::from(elem))),
598        )
599    };
600    ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => {
601        $builder.append_all($parent.$elem.into_iter())
602    };
603    ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => {
604        $builder.append(::minidom::Node::Element(
605            crate::Element::builder($name, crate::ns::$ns).build(),
606        ))
607    };
608    ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
609        $builder.append(::minidom::Node::Element(crate::Element::from(
610            $parent.$elem,
611        )))
612    };
613}
614
615macro_rules! generate_child_test {
616    ($child:ident, $name:tt, *) => {
617        $child.is($name, ::minidom::NSChoice::Any)
618    };
619    ($child:ident, $name:tt, $ns:tt) => {
620        $child.is($name, crate::ns::$ns)
621    };
622}
623
624macro_rules! generate_element {
625    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => (
626        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
627    );
628    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => (
629        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
630    );
631    ($(#[$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:tt) => $child_constructor:ident),*]) => (
632        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
633    );
634    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*]) => (
635        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
636    );
637    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
638        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
639    );
640    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+], text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
641        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
642    );
643    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:tt) => $child_constructor:ident),*] $(, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >))*) => (
644        $(#[$meta])*
645        #[derive(Debug, Clone, PartialEq)]
646        pub struct $elem {
647            $(
648                $(#[$attr_meta])*
649                pub $attr: decl_attr!($attr_action, $attr_type),
650            )*
651            $(
652                $(#[$child_meta])*
653                pub $child_ident: start_decl!($coucou, $child_type),
654            )*
655            $(
656                $(#[$text_meta])*
657                pub $text_ident: $text_type,
658            )*
659        }
660
661        impl ::std::convert::TryFrom<crate::Element> for $elem {
662            type Error = crate::util::error::Error;
663
664            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
665                check_self!(elem, $name, $ns);
666                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
667                $(
668                    start_parse_elem!($child_ident: $coucou);
669                )*
670                for _child in elem.children() {
671                    $(
672                    if generate_child_test!(_child, $child_name, $child_ns) {
673                        do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
674                        continue;
675                    }
676                    )*
677                    return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
678                }
679                Ok($elem {
680                    $(
681                        $attr: get_attr!(elem, $attr_name, $attr_action),
682                    )*
683                    $(
684                        $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
685                    )*
686                    $(
687                        $text_ident: $codec::decode(&elem.text())?,
688                    )*
689                })
690            }
691        }
692
693        impl From<$elem> for crate::Element {
694            fn from(elem: $elem) -> crate::Element {
695                let mut builder = crate::Element::builder($name, crate::ns::$ns);
696                $(
697                    builder = builder.attr($attr_name, elem.$attr);
698                )*
699                $(
700                    builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns));
701                )*
702                $(
703                    builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter());
704                )*
705
706                builder.build()
707            }
708        }
709    );
710}
711
712#[cfg(test)]
713macro_rules! assert_size (
714    ($t:ty, $sz:expr) => (
715        assert_eq!(::std::mem::size_of::<$t>(), $sz);
716    );
717);
718
719// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there.
720macro_rules! impl_pubsub_item {
721    ($item:ident, $ns:ident) => {
722        impl ::std::convert::TryFrom<crate::Element> for $item {
723            type Error = Error;
724
725            fn try_from(elem: crate::Element) -> Result<$item, Error> {
726                check_self!(elem, "item", $ns);
727                check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
728                let mut payloads = elem.children().cloned().collect::<Vec<_>>();
729                let payload = payloads.pop();
730                if !payloads.is_empty() {
731                    return Err(Error::ParseError(
732                        "More than a single payload in item element.",
733                    ));
734                }
735                Ok($item(crate::pubsub::Item {
736                    id: get_attr!(elem, "id", Option),
737                    publisher: get_attr!(elem, "publisher", Option),
738                    payload,
739                }))
740            }
741        }
742
743        impl From<$item> for crate::Element {
744            fn from(item: $item) -> crate::Element {
745                crate::Element::builder("item", ns::$ns)
746                    .attr("id", item.0.id)
747                    .attr("publisher", item.0.publisher)
748                    .append_all(item.0.payload)
749                    .build()
750            }
751        }
752
753        impl ::std::ops::Deref for $item {
754            type Target = crate::pubsub::Item;
755
756            fn deref(&self) -> &Self::Target {
757                &self.0
758            }
759        }
760
761        impl ::std::ops::DerefMut for $item {
762            fn deref_mut(&mut self) -> &mut Self::Target {
763                &mut self.0
764            }
765        }
766    };
767}