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