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        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<::minidom::Element> for $elem {
278            type Error = crate::util::error::Error;
279            fn try_from(elem: ::minidom::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 ::minidom::Element {
290            fn from(elem: $elem) -> ::minidom::Element {
291                ::minidom::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<::minidom::Element> for $elem {
381            type Error = crate::util::error::Error;
382
383            fn try_from(elem: ::minidom::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 ::minidom::Element {
392            fn from(_: $elem) -> ::minidom::Element {
393                ::minidom::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<::minidom::Element> for $elem {
443            type Error = crate::util::error::Error;
444            fn try_from(elem: ::minidom::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 ::minidom::Element {
453            fn from(elem: $elem) -> ::minidom::Element {
454                ::minidom::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}
498
499macro_rules! start_parse_elem {
500    ($temp:ident: Vec) => {
501        let mut $temp = Vec::new();
502    };
503    ($temp:ident: Option) => {
504        let mut $temp = None;
505    };
506    ($temp:ident: Required) => {
507        let mut $temp = None;
508    };
509}
510
511macro_rules! do_parse {
512    ($elem:ident, Element) => {
513        $elem.clone()
514    };
515    ($elem:ident, String) => {
516        $elem.text()
517    };
518    ($elem:ident, $constructor:ident) => {
519        $constructor::try_from($elem.clone())?
520    };
521}
522
523macro_rules! do_parse_elem {
524    ($temp:ident: Vec = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
525        $temp.push(do_parse!($elem, $constructor));
526    };
527    ($temp:ident: Option = $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: Required = $constructor:ident => $elem:ident, $name:tt, $parent_name:tt) => {
540        if $temp.is_some() {
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 = Some(do_parse!($elem, $constructor));
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}
570
571macro_rules! generate_serialiser {
572    ($builder:ident, $parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => {
573        $builder.append(
574            ::minidom::Element::builder($name)
575                .ns(crate::ns::$ns)
576                .append(::minidom::Node::Text($parent.$elem))
577        )
578    };
579    ($builder:ident, $parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => {
580        $builder.append_all($parent.$elem.map(|elem| {
581                ::minidom::Element::builder($name)
582                    .ns(crate::ns::$ns)
583                    .append(::minidom::Node::Text(elem))
584            })
585        )
586    };
587    ($builder:ident, $parent:ident, $elem:ident, Option, $constructor:ident, ($name:tt, $ns:ident)) => {
588        $builder.append_all($parent.$elem.map(|elem| {
589                ::minidom::Element::builder($name)
590                    .ns(crate::ns::$ns)
591                    .append(::minidom::Node::Element(::minidom::Element::from(elem)))
592            })
593        )
594    };
595    ($builder:ident, $parent:ident, $elem:ident, Vec, $constructor:ident, ($name:tt, $ns:ident)) => {
596        $builder.append_all($parent.$elem.into_iter())
597    };
598    ($builder:ident, $parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => {
599        $builder.append(::minidom::Node::Element(::minidom::Element::from($parent.$elem)))
600    };
601}
602
603macro_rules! generate_element {
604    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+,]) => (
605        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
606    );
607    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_action:tt<$attr_type:ty> = $attr_name:tt),+]) => (
608        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_action<$attr_type> = $attr_name),*], children: []);
609    );
610    ($(#[$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),*]) => (
611        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),*]);
612    );
613    ($(#[$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),*]) => (
614        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),*]);
615    );
616    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, text: ($(#[$text_meta:meta])* $text_ident:ident: $codec:ident < $text_type:ty >)) => (
617        generate_element!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [], text: ($(#[$text_meta])* $text_ident: $codec<$text_type>));
618    );
619    ($(#[$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 >)) => (
620        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>));
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),*], 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 >))*) => (
623        $(#[$meta])*
624        #[derive(Debug, Clone)]
625        pub struct $elem {
626            $(
627                $(#[$attr_meta])*
628                pub $attr: decl_attr!($attr_action, $attr_type),
629            )*
630            $(
631                $(#[$child_meta])*
632                pub $child_ident: start_decl!($coucou, $child_type),
633            )*
634            $(
635                $(#[$text_meta])*
636                pub $text_ident: $text_type,
637            )*
638        }
639
640        impl ::std::convert::TryFrom<::minidom::Element> for $elem {
641            type Error = crate::util::error::Error;
642
643            fn try_from(elem: ::minidom::Element) -> Result<$elem, crate::util::error::Error> {
644                check_self!(elem, $name, $ns);
645                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
646                $(
647                    start_parse_elem!($child_ident: $coucou);
648                )*
649                for _child in elem.children() {
650                    $(
651                    if _child.is($child_name, crate::ns::$child_ns) {
652                        do_parse_elem!($child_ident: $coucou = $child_constructor => _child, $child_name, $name);
653                        continue;
654                    }
655                    )*
656                    return Err(crate::util::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
657                }
658                Ok($elem {
659                    $(
660                        $attr: get_attr!(elem, $attr_name, $attr_action),
661                    )*
662                    $(
663                        $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name, $name),
664                    )*
665                    $(
666                        $text_ident: $codec::decode(&elem.text())?,
667                    )*
668                })
669            }
670        }
671
672        impl From<$elem> for ::minidom::Element {
673            fn from(elem: $elem) -> ::minidom::Element {
674                let mut builder = ::minidom::Element::builder($name)
675                    .ns(crate::ns::$ns);
676                $(
677                    builder = builder.attr($attr_name, elem.$attr);
678                )*
679                $(
680                    builder = generate_serialiser!(builder, elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns));
681                )*
682                $(
683                    builder = builder.append_all($codec::encode(&elem.$text_ident).map(::minidom::Node::Text).into_iter());
684                )*
685
686                builder.build()
687            }
688        }
689
690        impl From<$elem> for ::minidom::Node {
691            fn from(elem: $elem) -> ::minidom::Node {
692                ::minidom::Node::Element(elem.into())
693            }
694        }
695    );
696}
697
698#[cfg(test)]
699macro_rules! assert_size (
700    ($t:ty, $sz:expr) => (
701        assert_eq!(::std::mem::size_of::<$t>(), $sz);
702    );
703);
704
705// TODO: move that to src/pubsub/mod.rs, once we figure out how to use macros from there.
706macro_rules! impl_pubsub_item {
707    ($item:ident, $ns:ident) => {
708        impl ::std::convert::TryFrom<crate::Element> for $item {
709            type Error = Error;
710
711            fn try_from(elem: crate::Element) -> Result<$item, Error> {
712                check_self!(elem, "item", $ns);
713                check_no_unknown_attributes!(elem, "item", ["id", "publisher"]);
714                let mut payloads = elem.children().cloned().collect::<Vec<_>>();
715                let payload = payloads.pop();
716                if !payloads.is_empty() {
717                    return Err(Error::ParseError(
718                        "More than a single payload in item element.",
719                    ));
720                }
721                Ok($item(crate::pubsub::Item {
722                    id: get_attr!(elem, "id", Option),
723                    publisher: get_attr!(elem, "publisher", Option),
724                    payload,
725                }))
726            }
727        }
728
729        impl From<$item> for crate::Element {
730            fn from(item: $item) -> crate::Element {
731                crate::Element::builder("item")
732                    .ns(ns::$ns)
733                    .attr("id", item.0.id)
734                    .attr("publisher", item.0.publisher)
735                    .append_all(item.0.payload)
736                    .build()
737            }
738        }
739
740        impl From<$item> for ::minidom::Node {
741            fn from(item: $item) -> ::minidom::Node {
742                ::minidom::Node::Element(item.into())
743            }
744        }
745
746        impl ::std::ops::Deref for $item {
747            type Target = crate::pubsub::Item;
748
749            fn deref(&self) -> &Self::Target {
750                &self.0
751            }
752        }
753
754        impl ::std::ops::DerefMut for $item {
755            fn deref_mut(&mut self) -> &mut Self::Target {
756                &mut self.0
757            }
758        }
759    }
760}