macros: Merge all element children into a common syntax.

Emmanuel Gil Peyrot created

Change summary

src/jingle.rs        |  79 +++--------------
src/macros.rs        | 207 ++++++++++++++++++++-------------------------
src/mam.rs           | 112 ++++++------------------
src/muc/muc.rs       |  51 +---------
src/muc/user.rs      | 142 ++++++++----------------------
src/pubsub/pubsub.rs |  16 +-
src/sm.rs            |   4 
src/version.rs       |  79 ++---------------
8 files changed, 200 insertions(+), 490 deletions(-)

Detailed changes

src/jingle.rs 🔗

@@ -66,16 +66,20 @@ generate_attribute!(Disposition, "disposition", {
 
 generate_id!(ContentId);
 
-#[derive(Debug, Clone)]
-pub struct Content {
-    pub creator: Creator,
-    pub disposition: Disposition,
-    pub name: ContentId,
-    pub senders: Senders,
-    pub description: Option<Element>,
-    pub transport: Option<Element>,
-    pub security: Option<Element>,
-}
+generate_element_with_children!(
+    Content, "content", JINGLE,
+    attributes: [
+        creator: Creator = "creator" => required,
+        disposition: Disposition = "disposition" => default,
+        name: ContentId = "name" => required,
+        senders: Senders = "senders" => default
+    ],
+    children: [
+        description: Option<Element> = ("description", JINGLE) => Element,
+        transport: Option<Element> = ("transport", JINGLE) => Element,
+        security: Option<Element> = ("security", JINGLE) => Element
+    ]
+);
 
 impl Content {
     pub fn new(creator: Creator, name: ContentId) -> Content {
@@ -116,61 +120,6 @@ impl Content {
     }
 }
 
-impl TryFrom<Element> for Content {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Content, Error> {
-        check_self!(elem, "content", JINGLE);
-        check_no_unknown_attributes!(elem, "content", ["creator", "disposition", "name", "senders"]);
-
-        let mut content = Content {
-            creator: get_attr!(elem, "creator", required),
-            disposition: get_attr!(elem, "disposition", default),
-            name: get_attr!(elem, "name", required),
-            senders: get_attr!(elem, "senders", default),
-            description: None,
-            transport: None,
-            security: None,
-        };
-        for child in elem.children() {
-            if child.name() == "description" {
-                if content.description.is_some() {
-                    return Err(Error::ParseError("Content must not have more than one description."));
-                }
-                content.description = Some(child.clone());
-            } else if child.name() == "transport" {
-                if content.transport.is_some() {
-                    return Err(Error::ParseError("Content must not have more than one transport."));
-                }
-                content.transport = Some(child.clone());
-            } else if child.name() == "security" {
-                if content.security.is_some() {
-                    return Err(Error::ParseError("Content must not have more than one security."));
-                }
-                content.security = Some(child.clone());
-            } else {
-                return Err(Error::ParseError("Unknown child in content element."));
-            }
-        }
-        Ok(content)
-    }
-}
-
-impl From<Content> for Element {
-    fn from(content: Content) -> Element {
-        Element::builder("content")
-                .ns(ns::JINGLE)
-                .attr("creator", content.creator)
-                .attr("disposition", content.disposition)
-                .attr("name", content.name)
-                .attr("senders", content.senders)
-                .append(content.description)
-                .append(content.transport)
-                .append(content.security)
-                .build()
-    }
-}
-
 #[derive(Debug, Clone, PartialEq)]
 pub enum Reason {
     AlternativeSession, //(String),

src/macros.rs 🔗

@@ -424,129 +424,96 @@ macro_rules! generate_element_with_text {
     );
 }
 
-macro_rules! generate_element_with_children {
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => (
-        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]);
+macro_rules! start_decl {
+    (Vec, $type:ty) => (
+        Vec<$type>
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => (
-        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: Vec<$child_type> = ($child_name, $child_ns) => $child_constructor),+]);
+    (Option, $type:ty) => (
+        Option<$type>
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: Vec<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => (
-        $(#[$meta])*
-        #[derive(Debug, Clone)]
-        pub struct $elem {
-            $(
-            $(#[$attr_meta])*
-            pub $attr: $attr_type,
-            )*
-            $(
-            $(#[$child_meta])*
-            pub $child_ident: Vec<$child_type>,
-            )*
-        }
-
-        impl ::try_from::TryFrom<::minidom::Element> for $elem {
-            type Err = ::error::Error;
-
-            fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
-                check_self!(elem, $name, $ns);
-                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
-                let mut parsed_children = vec!();
-                for child in elem.children() {
-                    $(
-                    if child.is($child_name, ::ns::$child_ns) {
-                        let parsed_child = $child_constructor::try_from(child.clone())?;
-                        parsed_children.push(parsed_child);
-                        continue;
-                    }
-                    )*
-                    return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
-                }
-                Ok($elem {
-                    $(
-                    $attr: get_attr!(elem, $attr_name, $attr_action),
-                    )*
-                    $(
-                    $child_ident: parsed_children,
-                    )*
-                })
-            }
-        }
+    (Required, $type:ty) => (
+        $type
+    );
+}
 
-        impl From<$elem> for ::minidom::Element {
-            fn from(elem: $elem) -> ::minidom::Element {
-                ::minidom::Element::builder($name)
-                        .ns(::ns::$ns)
-                        $(
-                        .attr($attr_name, elem.$attr)
-                        )*
-                        $(
-                        .append(elem.$child_ident)
-                        )*
-                        .build()
-            }
-        }
+macro_rules! start_parse_elem {
+    ($temp:ident: Vec) => (
+        let mut $temp = Vec::new();
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => (
-        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor));
+    ($temp:ident: Option) => (
+        let mut $temp = None;
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => (
-        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: Option<$child_type> = ($child_name, $child_ns) => $child_constructor));
+    ($temp:ident: Required) => (
+        let mut $temp = None;
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: Option<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => (
-        $(#[$meta])*
-        #[derive(Debug, Clone)]
-        pub struct $elem {
-            $(
-            $(#[$attr_meta])*
-            pub $attr: $attr_type,
-            )*
-            $(#[$child_meta])*
-            pub $child_ident: Option<$child_type>,
-        }
+}
 
-        impl ::try_from::TryFrom<::minidom::Element> for $elem {
-            type Err = ::error::Error;
+macro_rules! do_parse {
+    ($elem:ident, Element) => (
+        $elem.clone()
+    );
+    ($elem:ident, String) => (
+        $elem.text()
+    );
+    ($elem:ident, $constructor:ident) => (
+        $constructor::try_from($elem.clone())?
+    );
+}
 
-            fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
-                check_self!(elem, $name, $ns);
-                check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
-                let mut parsed_child = None;
-                for child in elem.children() {
-                    if child.is($child_name, ::ns::$child_ns) {
-                        parsed_child = Some($child_constructor::try_from(child.clone())?);
-                        continue;
-                    }
-                    return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
-                }
-                Ok($elem {
-                    $(
-                    $attr: get_attr!(elem, $attr_name, $attr_action),
-                    )*
-                    $child_ident: parsed_child,
-                })
-            }
+macro_rules! do_parse_elem {
+    ($temp:ident: Vec = $constructor:ident => $elem:ident) => (
+        $temp.push(do_parse!($elem, $constructor));
+    );
+    ($temp:ident: Option = $constructor:ident => $elem:ident) => (
+        if $temp.is_some() {
+            return Err(::error::Error::ParseError(concat!("coucou", " must not have more than one ", "coucou", ".")));
         }
+        $temp = Some(do_parse!($elem, $constructor));
+    );
+    ($temp:ident: Required = $constructor:ident => $elem:ident) => (
+        $temp = Some(do_parse!($elem, $constructor));
+    );
+}
 
-        impl From<$elem> for ::minidom::Element {
-            fn from(elem: $elem) -> ::minidom::Element {
-                ::minidom::Element::builder($name)
-                        .ns(::ns::$ns)
-                        $(
-                        .attr($attr_name, elem.$attr)
-                        )*
-                        .append(elem.$child_ident)
-                        .build()
-            }
-        }
+macro_rules! finish_parse_elem {
+    ($temp:ident: Vec = $name:tt) => (
+        $temp
+    );
+    ($temp:ident: Option = $name:tt) => (
+        $temp
+    );
+    ($temp:ident: Required = $name:tt) => (
+        $temp.ok_or(::error::Error::ParseError(concat!("Missing child coucou in ", $name, " element.")))?
+    );
+}
+
+macro_rules! generate_serialiser {
+    ($parent:ident, $elem:ident, Required, String, ($name:tt, $ns:ident)) => (
+        ::minidom::Element::builder($name)
+            .ns(::ns::$ns)
+            .append($parent.$elem)
+            .build()
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => (
-        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor));
+    ($parent:ident, $elem:ident, Option, String, ($name:tt, $ns:ident)) => (
+        $parent.$elem.map(|elem|
+            ::minidom::Element::builder($name)
+                .ns(::ns::$ns)
+                .append(elem)
+                .build())
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => (
-        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], child: ($(#[$child_meta])* $child_ident: $child_type = ($child_name, $child_ns) => $child_constructor));
+    ($parent:ident, $elem:ident, $_:ident, $constructor:ident, ($name:tt, $ns:ident)) => (
+        $parent.$elem
     );
-    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], child: ($(#[$child_meta:meta])* $child_ident:ident: $child_type:ty = ($child_name:tt, $child_ns:ident) => $child_constructor:ident)) => (
+}
+
+macro_rules! generate_element_with_children {
+    ($(#[$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),+]) => (
+        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]);
+    );
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*,], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => (
+        generate_element_with_children!($(#[$meta])* $elem, $name, $ns, attributes: [$($(#[$attr_meta])* $attr: $attr_type = $attr_name => $attr_action),*], children: [$($(#[$child_meta])* $child_ident: $coucou<$child_type> = ($child_name, $child_ns) => $child_constructor),+]);
+    );
+    ($(#[$meta:meta])* $elem:ident, $name:tt, $ns:ident, attributes: [$($(#[$attr_meta:meta])* $attr:ident: $attr_type:ty = $attr_name:tt => $attr_action:tt),*], children: [$($(#[$child_meta:meta])* $child_ident:ident: $coucou:tt<$child_type:ty> = ($child_name:tt, $child_ns:ident) => $child_constructor:ident),+]) => (
         $(#[$meta])*
         #[derive(Debug, Clone)]
         pub struct $elem {
@@ -554,8 +521,10 @@ macro_rules! generate_element_with_children {
             $(#[$attr_meta])*
             pub $attr: $attr_type,
             )*
+            $(
             $(#[$child_meta])*
-            pub $child_ident: $child_type,
+            pub $child_ident: start_decl!($coucou, $child_type),
+            )*
         }
 
         impl ::try_from::TryFrom<::minidom::Element> for $elem {
@@ -564,19 +533,25 @@ macro_rules! generate_element_with_children {
             fn try_from(elem: ::minidom::Element) -> Result<$elem, ::error::Error> {
                 check_self!(elem, $name, $ns);
                 check_no_unknown_attributes!(elem, $name, [$($attr_name),*]);
-                let mut parsed_child = None;
+                $(
+                start_parse_elem!($child_ident: $coucou);
+                )*
                 for child in elem.children() {
+                    $(
                     if child.is($child_name, ::ns::$child_ns) {
-                        parsed_child = Some($child_constructor::try_from(child.clone())?);
+                        do_parse_elem!($child_ident: $coucou = $child_constructor => child);
                         continue;
                     }
+                    )*
                     return Err(::error::Error::ParseError(concat!("Unknown child in ", $name, " element.")));
                 }
                 Ok($elem {
                     $(
                     $attr: get_attr!(elem, $attr_name, $attr_action),
                     )*
-                    $child_ident: parsed_child.ok_or(::error::Error::ParseError(concat!("Missing child ", $child_name, " in ", $name, " element.")))?,
+                    $(
+                    $child_ident: finish_parse_elem!($child_ident: $coucou = $child_name),
+                    )*
                 })
             }
         }
@@ -588,7 +563,9 @@ macro_rules! generate_element_with_children {
                         $(
                         .attr($attr_name, elem.$attr)
                         )*
-                        .append(elem.$child_ident)
+                        $(
+                        .append(generate_serialiser!(elem, $child_ident, $coucou, $child_constructor, ($child_name, $child_ns)))
+                        )*
                         .build()
             }
         }

src/mam.rs 🔗

@@ -18,13 +18,17 @@ use forwarding::Forwarded;
 
 use ns;
 
-#[derive(Debug, Clone)]
-pub struct Query {
-    pub queryid: Option<String>,
-    pub node: Option<String>,
-    pub form: Option<DataForm>,
-    pub set: Option<Set>,
-}
+generate_element_with_children!(
+    Query, "query", MAM,
+    attributes: [
+        queryid: Option<String> = "queryid" => optional,
+        node: Option<String> = "node" => optional
+    ],
+    children: [
+        form: Option<DataForm> = ("x", DATA_FORMS) => DataForm,
+        set: Option<Set> = ("set", RSM) => Set
+    ]
+);
 
 impl IqGetPayload for Query {}
 impl IqSetPayload for Query {}
@@ -36,16 +40,24 @@ generate_element_with_children!(
         id: String = "id" => required,
         queryid: String = "queryid" => required,
     ],
-    child: (
-        forwarded: Forwarded = ("forwarded", FORWARD) => Forwarded
-    )
+    children: [
+        forwarded: Required<Forwarded> = ("forwarded", FORWARD) => Forwarded
+    ]
 );
 
-#[derive(Debug, Clone)]
-pub struct Fin {
-    pub complete: bool,
-    pub set: Set,
-}
+generate_attribute!(
+    Complete, "complete", bool
+);
+
+generate_element_with_children!(
+    Fin, "fin", MAM,
+    attributes: [
+        complete: Complete = "complete" => default
+    ],
+    children: [
+        set: Required<Set> = ("set", RSM) => Set
+    ]
+);
 
 impl IqResultPayload for Fin {}
 
@@ -66,54 +78,6 @@ impl IqGetPayload for Prefs {}
 impl IqSetPayload for Prefs {}
 impl IqResultPayload for Prefs {}
 
-impl TryFrom<Element> for Query {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Query, Error> {
-        check_self!(elem, "query", MAM);
-        check_no_unknown_attributes!(elem, "query", ["queryid", "node"]);
-        let mut form = None;
-        let mut set = None;
-        for child in elem.children() {
-            if child.is("x", ns::DATA_FORMS) {
-                form = Some(DataForm::try_from(child.clone())?);
-            } else if child.is("set", ns::RSM) {
-                set = Some(Set::try_from(child.clone())?);
-            } else {
-                return Err(Error::ParseError("Unknown child in query element."));
-            }
-        }
-        let queryid = get_attr!(elem, "queryid", optional);
-        let node = get_attr!(elem, "node", optional);
-        Ok(Query { queryid, node, form, set })
-    }
-}
-
-impl TryFrom<Element> for Fin {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Fin, Error> {
-        check_self!(elem, "fin", MAM);
-        check_no_unknown_attributes!(elem, "fin", ["complete"]);
-        let mut set = None;
-        for child in elem.children() {
-            if child.is("set", ns::RSM) {
-                set = Some(Set::try_from(child.clone())?);
-            } else {
-                return Err(Error::ParseError("Unknown child in fin element."));
-            }
-        }
-        let set = set.ok_or(Error::ParseError("Mandatory set element missing in fin."))?;
-        let complete = match elem.attr("complete") {
-            Some(complete) if complete == "true" => true,
-            Some(complete) if complete == "false" => false,
-            None => false,
-            Some(_) => return Err(Error::ParseError("Invalid value for 'complete' attribute.")),
-        };
-        Ok(Fin { complete, set })
-    }
-}
-
 impl TryFrom<Element> for Prefs {
     type Err = Error;
 
@@ -146,28 +110,6 @@ impl TryFrom<Element> for Prefs {
     }
 }
 
-impl From<Query> for Element {
-    fn from(query: Query) -> Element {
-        Element::builder("query")
-                .ns(ns::MAM)
-                .attr("queryid", query.queryid)
-                .attr("node", query.node)
-                //.append(query.form)
-                .append(query.set)
-                .build()
-    }
-}
-
-impl From<Fin> for Element {
-    fn from(fin: Fin) -> Element {
-        Element::builder("fin")
-                .ns(ns::MAM)
-                .attr("complete", if fin.complete { Some("true") } else { None })
-                .append(fin.set)
-                .build()
-    }
-}
-
 fn serialise_jid_list(name: &str, jids: Vec<Jid>) -> Option<Element> {
     if jids.is_empty() {
         None

src/muc/muc.rs 🔗

@@ -5,53 +5,18 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-use try_from::TryFrom;
-
-use minidom::Element;
-
-use error::Error;
-
-use ns;
-
-#[derive(Debug, Clone)]
-pub struct Muc {
-    pub password: Option<String>,
-}
-
-impl TryFrom<Element> for Muc {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Muc, Error> {
-        check_self!(elem, "x", MUC);
-        check_no_attributes!(elem, "x");
-
-        let mut password = None;
-        for child in elem.children() {
-            if child.is("password", ns::MUC) {
-                password = Some(child.text());
-            } else {
-                return Err(Error::ParseError("Unknown child in x element."));
-            }
-        }
-
-        Ok(Muc {
-            password: password,
-        })
-    }
-}
-
-impl From<Muc> for Element {
-    fn from(muc: Muc) -> Element {
-        Element::builder("x")
-                .ns(ns::MUC)
-                .append(muc.password)
-                .build()
-    }
-}
+generate_element_with_children!(
+    Muc, "x", MUC, children: [
+        password: Option<String> = ("password", MUC) => String
+    ]
+);
 
 #[cfg(test)]
 mod tests {
     use super::*;
+    use try_from::TryFrom;
+    use minidom::Element;
+    use error::Error;
     use compare_elements::NamespaceAwareCompare;
 
     #[test]

src/muc/user.rs 🔗

@@ -5,7 +5,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-use try_from::{TryFrom, TryInto};
+use try_from::TryFrom;
 
 use minidom::Element;
 
@@ -80,6 +80,7 @@ Status, "status", MUC_USER, "code", {
 /// Optional <actor/> element used in <item/> elements inside presence stanzas of type
 /// "unavailable" that are sent to users who are kick or banned, as well as within IQs for tracking
 /// purposes. -- CHANGELOG  0.17 (2002-10-23)
+///
 /// Possesses a 'jid' and a 'nick' attribute, so that an action can be attributed either to a real
 /// JID or to a roomnick. -- CHANGELOG  1.25 (2012-02-08)
 #[derive(Debug, Clone, PartialEq)]
@@ -140,108 +141,25 @@ generate_attribute!(Role, "role", {
     None => "none",
 }, Default = None);
 
-#[derive(Debug, Clone)]
-pub struct Item {
-    pub affiliation: Affiliation,
-    pub jid: Option<Jid>,
-    pub nick: Option<String>,
-    pub role: Role,
-    pub actor: Option<Actor>,
-    pub continue_: Option<Continue>,
-    pub reason: Option<Reason>,
-}
-
-impl TryFrom<Element> for Item {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Item, Error> {
-        check_self!(elem, "item", MUC_USER);
-        check_no_unknown_attributes!(elem, "item", ["affiliation", "jid", "nick", "role"]);
-        let mut actor: Option<Actor> = None;
-        let mut continue_: Option<Continue> = None;
-        let mut reason: Option<Reason> = None;
-        for child in elem.children() {
-            if child.is("actor", ns::MUC_USER) {
-                actor = Some(child.clone().try_into()?);
-            } else if child.is("continue", ns::MUC_USER) {
-                continue_ = Some(child.clone().try_into()?);
-            } else if child.is("reason", ns::MUC_USER) {
-                reason = Some(child.clone().try_into()?);
-            } else {
-                return Err(Error::ParseError("Unknown child in item element."));
-            }
-        }
-
-        let affiliation: Affiliation = get_attr!(elem, "affiliation", required);
-        let jid: Option<Jid> = get_attr!(elem, "jid", optional);
-        let nick: Option<String> = get_attr!(elem, "nick", optional);
-        let role: Role = get_attr!(elem, "role", required);
-
-        Ok(Item{
-            affiliation: affiliation,
-            jid: jid,
-            nick: nick,
-            role: role,
-            actor: actor,
-            continue_: continue_,
-            reason: reason,
-        })
-    }
-}
-
-impl From<Item> for Element {
-    fn from(item: Item) -> Element {
-        Element::builder("item")
-                .ns(ns::MUC_USER)
-                .attr("affiliation", item.affiliation)
-                .attr("jid", item.jid)
-                .attr("nick", item.nick)
-                .attr("role", item.role)
-                .append(item.actor)
-                .append(item.continue_)
-                .append(item.reason)
-                .build()
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct MucUser {
-    pub status: Vec<Status>,
-    pub items: Vec<Item>,
-}
-
-impl TryFrom<Element> for MucUser {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<MucUser, Error> {
-        check_self!(elem, "x", MUC_USER);
-        check_no_attributes!(elem, "x");
-        let mut status = vec!();
-        let mut items = vec!();
-        for child in elem.children() {
-            if child.is("status", ns::MUC_USER) {
-                status.push(Status::try_from(child.clone())?);
-            } else if child.is("item", ns::MUC_USER) {
-                items.push(Item::try_from(child.clone())?);
-            } else {
-                return Err(Error::ParseError("Unknown child in x element."));
-            }
-        }
-        Ok(MucUser {
-            status,
-            items,
-        })
-    }
-}
-
-impl From<MucUser> for Element {
-    fn from(muc_user: MucUser) -> Element {
-        Element::builder("x")
-                .ns(ns::MUC_USER)
-                .append(muc_user.status)
-                .build()
-    }
-}
+generate_element_with_children!(
+    Item, "item", MUC_USER, attributes: [
+        affiliation: Affiliation = "affiliation" => required,
+        jid: Option<Jid> = "jid" => optional,
+        nick: Option<String> = "nick" => optional,
+        role: Role = "role" => required
+    ], children: [
+        actor: Option<Actor> = ("actor", MUC_USER) => Actor,
+        continue_: Option<Continue> = ("continue", MUC_USER) => Continue,
+        reason: Option<Reason> = ("reason", MUC_USER) => Reason
+    ]
+);
+
+generate_element_with_children!(
+    MucUser, "x", MUC_USER, children: [
+        status: Vec<Status> = ("status", MUC_USER) => Status,
+        items: Vec<Item> = ("item", MUC_USER) => Item
+    ]
+);
 
 #[cfg(test)]
 mod tests {
@@ -257,6 +175,24 @@ mod tests {
         MucUser::try_from(elem).unwrap();
     }
 
+    #[test]
+    fn statuses_and_items() {
+        let elem: Element = "
+            <x xmlns='http://jabber.org/protocol/muc#user'>
+                <status code='101'/>
+                <status code='102'/>
+                <item affiliation='member' role='moderator'/>
+            </x>
+        ".parse().unwrap();
+        let muc_user = MucUser::try_from(elem).unwrap();
+        assert_eq!(muc_user.status.len(), 2);
+        assert_eq!(muc_user.status[0], Status::AffiliationChange);
+        assert_eq!(muc_user.status[1], Status::ConfigShowsUnavailableMembers);
+        assert_eq!(muc_user.items.len(), 1);
+        assert_eq!(muc_user.items[0].affiliation, Affiliation::Member);
+        assert_eq!(muc_user.items[0].role, Role::Moderator);
+    }
+
     #[test]
     fn test_invalid_child() {
         let elem: Element = "

src/pubsub/pubsub.rs 🔗

@@ -72,10 +72,10 @@ generate_element_with_only_attributes!(
 generate_element_with_children!(
     /// Request to configure a new node.
     Configure, "configure", PUBSUB,
-    child: (
+    children: [
         /// The form to configure it.
         form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
-    )
+    ]
 );
 
 generate_element_with_only_attributes!(
@@ -168,10 +168,10 @@ generate_element_with_children!(
         /// The subscription identifier affected by this request.
         subid: Option<SubscriptionId> = "subid" => optional,
     ],
-    child: (
+    children: [
         /// The form describing the subscription.
         form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
-    )
+    ]
 );
 
 generate_element_with_children!(
@@ -190,10 +190,10 @@ generate_element_with_children!(
 generate_element_with_children!(
     /// The options associated to a publish request.
     PublishOptions, "publish-options", PUBSUB,
-    child: (
+    children: [
         /// The form describing these options.
         form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
-    )
+    ]
 );
 
 generate_attribute!(
@@ -300,10 +300,10 @@ generate_element_with_children!(
         /// The state of the subscription.
         subscription: Option<Subscription> = "subscription" => optional,
     ],
-    child: (
+    children: [
         /// The options related to this subscription.
         subscribe_options: Option<SubscribeOptions> = ("subscribe-options", PUBSUB) => SubscribeOptions
-    )
+    ]
 );
 
 generate_element_with_only_attributes!(

src/sm.rs 🔗

@@ -62,10 +62,10 @@ generate_element_with_children!(
     attributes: [
         h: Option<u32> = "h" => optional,
     ],
-    child: (
+    children: [
         // XXX: implement the * handling.
         error: Option<DefinedCondition> = ("*", XMPP_STANZAS) => DefinedCondition
-    )
+    ]
 );
 
 generate_empty_element!(

src/version.rs 🔗

@@ -4,84 +4,25 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-use try_from::TryFrom;
-use minidom::Element;
-use error::Error;
-use ns;
 use iq::{IqGetPayload, IqResultPayload};
 
-#[derive(Debug, Clone)]
-pub struct Version {
-    pub name: String,
-    pub version: String,
-    pub os: Option<String>,
-}
+generate_element_with_children!(
+    Version, "query", VERSION,
+    children: [
+        name: Required<String> = ("name", VERSION) => String,
+        version: Required<String> = ("version", VERSION) => String,
+        os: Option<String> = ("os", VERSION) => String
+    ]
+);
 
 impl IqGetPayload for Version {}
 impl IqResultPayload for Version {}
 
-impl TryFrom<Element> for Version {
-    type Err = Error;
-
-    fn try_from(elem: Element) -> Result<Version, Error> {
-        check_self!(elem, "query", VERSION, "version");
-        check_no_attributes!(elem, "version");
-        let mut name = None;
-        let mut version = None;
-        let mut os = None;
-        for child in elem.children() {
-            if child.is("name", ns::VERSION) {
-                if name.is_some() {
-                    return Err(Error::ParseError("More than one name in version element."));
-                }
-                name = Some(child.text());
-            } else if child.is("version", ns::VERSION) {
-                if version.is_some() {
-                    return Err(Error::ParseError("More than one version in version element."));
-                }
-                version = Some(child.text());
-            } else if child.is("os", ns::VERSION) {
-                if os.is_some() {
-                    return Err(Error::ParseError("More than one os in version element."));
-                }
-                os = Some(child.text());
-            } else {
-                return Err(Error::ParseError("Unknown child in version element."));
-            }
-        }
-        let name = name.unwrap();
-        let version = version.unwrap();
-        Ok(Version {
-            name,
-            version,
-            os,
-        })
-    }
-}
-
-impl From<Version> for Element {
-    fn from(version: Version) -> Element {
-        Element::builder("query")
-                .ns(ns::VERSION)
-                .append(Element::builder("name")
-                                .ns(ns::VERSION)
-                                .append(version.name)
-                                .build())
-                .append(Element::builder("version")
-                                .ns(ns::VERSION)
-                                .append(version.version)
-                                .build())
-                .append(Element::builder("os")
-                                .ns(ns::VERSION)
-                                .append(version.os)
-                                .build())
-                .build()
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
+    use try_from::TryFrom;
+    use minidom::Element;
     use compare_elements::NamespaceAwareCompare;
 
     #[test]