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