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<crate::Element> for $elem {
234            type Error = crate::util::error::Error;
235            fn try_from(elem: crate::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 crate::Element {
246            fn from(elem: $elem) -> crate::Element {
247                crate::Element::builder(
248                    match elem {
249                        $($elem::$enum => $enum_name,)+
250                    }
251                )
252                    .ns(crate::ns::$ns)
253                    .build()
254            }
255        }
256        impl From<$elem> for ::minidom::Node {
257            fn from(elem: $elem) -> ::minidom::Node {
258                ::minidom::Node::Element(elem.into())
259            }
260        }
261    );
262}
263
264macro_rules! generate_attribute_enum {
265    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+,}) => (
266        generate_attribute_enum!($(#[$meta])* $elem, $name, $ns, $attr, {$($(#[$enum_meta])* $enum => $enum_name),+});
267    );
268    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $attr:tt, {$($(#[$enum_meta:meta])* $enum:ident => $enum_name:tt),+}) => (
269        $(#[$meta])*
270        #[derive(Debug, Clone, PartialEq)]
271        pub enum $elem {
272            $(
273                $(#[$enum_meta])*
274                $enum
275            ),+
276        }
277        impl ::std::convert::TryFrom<crate::Element> for $elem {
278            type Error = crate::util::error::Error;
279            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
280                check_ns_only!(elem, $name, $ns);
281                check_no_children!(elem, $name);
282                check_no_unknown_attributes!(elem, $name, [$attr]);
283                Ok(match get_attr!(elem, $attr, Required) {
284                    $($enum_name => $elem::$enum,)+
285                    _ => return Err(crate::util::error::Error::ParseError(concat!("Invalid ", $name, " ", $attr, " value."))),
286                })
287            }
288        }
289        impl From<$elem> for crate::Element {
290            fn from(elem: $elem) -> crate::Element {
291                crate::Element::builder($name)
292                    .ns(crate::ns::$ns)
293                    .attr($attr, match elem {
294                         $($elem::$enum => $enum_name,)+
295                     })
296                     .build()
297            }
298        }
299        impl From<$elem> for ::minidom::Node {
300            fn from(elem: $elem) -> ::minidom::Node {
301                ::minidom::Node::Element(elem.into())
302            }
303        }
304    );
305}
306
307macro_rules! check_self {
308    ($elem:ident, $name:tt, $ns:ident) => {
309        check_self!($elem, $name, $ns, $name);
310    };
311    ($elem:ident, $name:tt, $ns:ident, $pretty_name:tt) => {
312        if !$elem.is($name, crate::ns::$ns) {
313            return Err(crate::util::error::Error::ParseError(concat!(
314                "This is not a ",
315                $pretty_name,
316                " element."
317            )));
318        }
319    };
320}
321
322macro_rules! check_ns_only {
323    ($elem:ident, $name:tt, $ns:ident) => {
324        if !$elem.has_ns(crate::ns::$ns) {
325            return Err(crate::util::error::Error::ParseError(concat!(
326                "This is not a ",
327                $name,
328                " element."
329            )));
330        }
331    };
332}
333
334macro_rules! check_no_children {
335    ($elem:ident, $name:tt) => {
336        #[cfg(not(feature = "disable-validation"))]
337        for _ in $elem.children() {
338            return Err(crate::util::error::Error::ParseError(concat!(
339                "Unknown child in ",
340                $name,
341                " element."
342            )));
343        }
344    };
345}
346
347macro_rules! check_no_attributes {
348    ($elem:ident, $name:tt) => {
349        #[cfg(not(feature = "disable-validation"))]
350        for _ in $elem.attrs() {
351            return Err(crate::util::error::Error::ParseError(concat!(
352                "Unknown attribute in ",
353                $name,
354                " element."
355            )));
356        }
357    };
358}
359
360macro_rules! check_no_unknown_attributes {
361    ($elem:ident, $name:tt, [$($attr:tt),*]) => (
362        #[cfg(not(feature = "disable-validation"))]
363        for (_attr, _) in $elem.attrs() {
364            $(
365                if _attr == $attr {
366                    continue;
367                }
368            )*
369            return Err(crate::util::error::Error::ParseError(concat!("Unknown attribute in ", $name, " element.")));
370        }
371    );
372}
373
374macro_rules! generate_empty_element {
375    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
376        $(#[$meta])*
377        #[derive(Debug, Clone)]
378        pub struct $elem;
379
380        impl ::std::convert::TryFrom<crate::Element> for $elem {
381            type Error = crate::util::error::Error;
382
383            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
384                check_self!(elem, $name, $ns);
385                check_no_children!(elem, $name);
386                check_no_attributes!(elem, $name);
387                Ok($elem)
388            }
389        }
390
391        impl From<$elem> for crate::Element {
392            fn from(_: $elem) -> crate::Element {
393                crate::Element::builder($name)
394                    .ns(crate::ns::$ns)
395                    .build()
396            }
397        }
398
399        impl From<$elem> for ::minidom::Node {
400            fn from(elem: $elem) -> ::minidom::Node {
401                ::minidom::Node::Element(elem.into())
402            }
403        }
404    );
405}
406
407macro_rules! generate_id {
408    ($(#[$meta:meta])* $elem:ident) => (
409        $(#[$meta])*
410        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
411        pub struct $elem(pub String);
412        impl ::std::str::FromStr for $elem {
413            type Err = crate::util::error::Error;
414            fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
415                // TODO: add a way to parse that differently when needed.
416                Ok($elem(String::from(s)))
417            }
418        }
419        impl ::minidom::IntoAttributeValue for $elem {
420            fn into_attribute_value(self) -> Option<String> {
421                Some(self.0)
422            }
423        }
424    );
425}
426
427macro_rules! generate_elem_id {
428    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident) => (
429        generate_elem_id!($(#[$meta])* $elem, $name, $ns, String);
430        impl ::std::str::FromStr for $elem {
431            type Err = crate::util::error::Error;
432            fn from_str(s: &str) -> Result<$elem, crate::util::error::Error> {
433                // TODO: add a way to parse that differently when needed.
434                Ok($elem(String::from(s)))
435            }
436        }
437    );
438    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, $type:ty) => (
439        $(#[$meta])*
440        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
441        pub struct $elem(pub $type);
442        impl ::std::convert::TryFrom<crate::Element> for $elem {
443            type Error = crate::util::error::Error;
444            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
445                check_self!(elem, $name, $ns);
446                check_no_children!(elem, $name);
447                check_no_attributes!(elem, $name);
448                // TODO: add a way to parse that differently when needed.
449                Ok($elem(elem.text().parse()?))
450            }
451        }
452        impl From<$elem> for crate::Element {
453            fn from(elem: $elem) -> crate::Element {
454                crate::Element::builder($name)
455                    .ns(crate::ns::$ns)
456                    .append(elem.0.to_string())
457                    .build()
458            }
459        }
460
461        impl From<$elem> for ::minidom::Node {
462            fn from(elem: $elem) -> ::minidom::Node {
463                ::minidom::Node::Element(elem.into())
464            }
465        }
466    );
467}
468
469macro_rules! decl_attr {
470    (OptionEmpty, $type:ty) => (
471        Option<$type>
472    );
473    (Option, $type:ty) => (
474        Option<$type>
475    );
476    (Required, $type:ty) => (
477        $type
478    );
479    (RequiredNonEmpty, $type:ty) => (
480        $type
481    );
482    (Default, $type:ty) => (
483        $type
484    );
485}
486
487macro_rules! start_decl {
488    (Vec, $type:ty) => (
489        Vec<$type>
490    );
491    (Option, $type:ty) => (
492        Option<$type>
493    );
494    (Required, $type:ty) => (
495        $type
496    );
497    (Present, $type:ty) => (
498        bool
499    );
500}
501
502macro_rules! start_parse_elem {
503    ($temp:ident: Vec) => {
504        let mut $temp = Vec::new();
505    };
506    ($temp:ident: Option) => {
507        let mut $temp = None;
508    };
509    ($temp:ident: Required) => {
510        let mut $temp = None;
511    };
512    ($temp:ident: Present) => {
513        let mut $temp = false;
514    };
515}
516
517macro_rules! do_parse {
518    ($elem:ident, Element) => {
519        $elem.clone()
520    };
521    ($elem:ident, String) => {
522        $elem.text()
523    };
524    ($elem:ident, $constructor:ident) => {
525        $constructor::try_from($elem.clone())?
526    };
527}
528
529macro_rules! do_parse_elem {
530    ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
531        $temp.push(do_parse!($elem, $constructor));
532    };
533    ($temp:ident: Option = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
534        if $temp.is_some() {
535            return Err(crate::util::error::Error::ParseError(concat!(
536                "Element ",
537                $parent_name,
538                " must not have more than one ",
539                $name,
540                " child."
541            )));
542        }
543        $temp = Some(do_parse!($elem, $constructor));
544    };
545    ($temp:ident: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
546        if $temp.is_some() {
547            return Err(crate::util::error::Error::ParseError(concat!(
548                "Element ",
549                $parent_name,
550                " must not have more than one ",
551                $name,
552                " child."
553            )));
554        }
555        $temp = Some(do_parse!($elem, $constructor));
556    };
557    ($temp:ident: Present = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
558        if $temp {
559            return Err(crate::util::error::Error::ParseError(concat!(
560                "Element ",
561                $parent_name,
562                " must not have more than one ",
563                $name,
564                " child."
565            )));
566        }
567        $temp = true;
568    };
569}
570
571macro_rules! finish_parse_elem {
572    ($temp:ident: Vec = $name:tt, $parent_name:tt) => {
573        $temp
574    };
575    ($temp:ident: Option = $name:tt, $parent_name:tt) => {
576        $temp
577    };
578    ($temp:ident: Required = $name:tt, $parent_name:tt) => {
579        $temp.ok_or(crate::util::error::Error::ParseError(concat!(
580            "Missing child ",
581            $name,
582            " in ",
583            $parent_name,
584            " element."
585        )))?
586    };
587    ($temp:ident: Present = $name:tt, $parent_name:tt) => {
588        $temp
589    };
590}
591
592macro_rules! generate_serialiser {
593    ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
594        $builder.append(
595            crate::Element::builder($name)
596                .ns(crate::ns::$ns)
597                .append(::minidom::Node::Text($parent.$elem))
598        )
599    };
600    ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
601        $builder.append_all($parent.$elem.map(|elem| {
602                crate::Element::builder($name)
603                    .ns(crate::ns::$ns)
604                    .append(::minidom::Node::Text(elem))
605            })
606        )
607    };
608    ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, *)) => {
609        $builder.append_all($parent.$elem.map(|elem| {
610                crate::Element::builder($name)
611                    .ns(elem.get_ns())
612                    .append(::minidom::Node::Element(crate::Element::from(elem)))
613            })
614        )
615    };
616    ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => {
617        $builder.append_all($parent.$elem.map(|elem| {
618                crate::Element::builder($name)
619                    .ns(crate::ns::$ns)
620                    .append(::minidom::Node::Element(crate::Element::from(elem)))
621            })
622        )
623    };
624    ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => {
625        $builder.append_all($parent.$elem.into_iter())
626    };
627    ($builder:ident, $parent:ident, $elem:ident, Present, $constructor:ident, ($name:tt, $ns:ident)) => {
628        $builder.append(::minidom::Node::Element(crate::Element::builder($name).ns(crate::ns::$ns).build()))
629    };
630    ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
631        $builder.append(::minidom::Node::Element(crate::Element::from($parent.$elem)))
632    };
633}
634
635macro_rules! generate_child_test {
636    ($child:ident, $name:tt, *) => {
637        true
638    };
639    ($child:ident, $name:tt, $ns:tt) => {
640        $child.is($name, crate::ns::$ns)
641    };
642}
643
644macro_rules! generate_element {
645    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => (
646        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
647    );
648    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => (
649        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
650    );
651    ($(#[$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),*]) => (
652        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
653    );
654    ($(#[$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),*]) => (
655        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),*]);
656    );
657    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
658        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
659    );
660    ($(#[$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 >)) => (
661        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>));
662    );
663    ($(#[$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 >))*) => (
664        $(#[$meta])*
665        #[derive(Debug, Clone)]
666        pub struct $elem {
667            $(
668                $(#[$attr_meta])*
669                pub $attr: decl_attr!($attr_action, $attr_type),
670            )*
671            $(
672                $(#[$child_meta])*
673                pub $child_ident: start_decl!($coucou, $child_type),
674            )*
675            $(
676                $(#[$text_meta])*
677                pub $text_ident: $text_type,
678            )*
679        }
680
681        impl ::std::convert::TryFrom<crate::Element> for $elem {
682            type Error = crate::util::error::Error;
683
684            fn try_from(elem: crate::Element) -> Result<$elem, crate::util::error::Error> {
685                check_self!(elem, $name, $ns);
686                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
687                $(
688                    start_parse_elem!($child_ident: $coucou);
689                )*
690                for _child in elem.children() {
691                    $(
692                    if generate_child_test!(_child, $child_name, $child_ns) {
693                        do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
694                        continue;
695                    }
696                    )*
697                    return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
698                }
699                Ok($elem {
700                    $(
701                        $attr: get_attr!(elem, $attr_name, $attr_action),
702                    )*
703                    $(
704                        $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
705                    )*
706                    $(
707                        $text_ident: $codec::decode(&elem.text())?,
708                    )*
709                })
710            }
711        }
712
713        impl From<$elem> for crate::Element {
714            fn from(elem: $elem) -> crate::Element {
715                let mut builder = crate::Element::builder($name)
716                    .ns(crate::ns::$ns);
717                $(
718                    builder = builder.attr($attr_name, elem.$attr);
719                )*
720                $(
721                    builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns));
722                )*
723                $(
724                    builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter());
725                )*
726
727                builder.build()
728            }
729        }
730
731        impl From<$elem> for ::minidom::Node {
732            fn from(elem: $elem) -> ::minidom::Node {
733                ::minidom::Node::Element(elem.into())
734            }
735        }
736    );
737}
738
739#[cfg(test)]
740macro_rules! assert_size (
741    ($t:ty, $sz:expr) => (
742        assert_eq!(::std::mem::size_of::<$t>(), $sz);
743    );
744);
745
746// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there.
747macro_rules! impl_pubsub_item {
748    ($item:ident, $ns:ident) => {
749        impl ::std::convert::TryFrom<crate::Element> for $item {
750            type Error = Error;
751
752            fn try_from(elem: crate::Element) -> Result<$item, Error> {
753                check_self!(elem, "item", $ns);
754                check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
755                let mut payloads = elem.children().cloned().collect::<Vec<_>>();
756                let payload = payloads.pop();
757                if !payloads.is_empty() {
758                    return Err(Error::ParseError(
759                        "More than a single payload in item element.",
760                    ));
761                }
762                Ok($item(crate::pubsub::Item {
763                    id: get_attr!(elem, "id", Option),
764                    publisher: get_attr!(elem, "publisher", Option),
765                    payload,
766                }))
767            }
768        }
769
770        impl From<$item> for crate::Element {
771            fn from(item: $item) -> crate::Element {
772                crate::Element::builder("item")
773                    .ns(ns::$ns)
774                    .attr("id", item.0.id)
775                    .attr("publisher", item.0.publisher)
776                    .append_all(item.0.payload)
777                    .build()
778            }
779        }
780
781        impl From<$item> for ::minidom::Node {
782            fn from(item: $item) -> ::minidom::Node {
783                ::minidom::Node::Element(item.into())
784            }
785        }
786
787        impl ::std::ops::Deref for $item {
788            type Target = crate::pubsub::Item;
789
790            fn deref(&self) -> &Self::Target {
791                &self.0
792            }
793        }
794
795        impl ::std::ops::DerefMut for $item {
796            fn deref_mut(&mut self) -> &mut Self::Target {
797                &mut self.0
798            }
799        }
800    }
801}