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