roster: Simplify parsing of Item.

Emmanuel Gil Peyrot created

Change summary

src/macros.rs | 17 ++++++++++++
src/roster.rs | 74 ++++++++++++----------------------------------------
2 files changed, 35 insertions(+), 56 deletions(-)

Detailed changes

src/macros.rs 🔗

@@ -8,6 +8,13 @@ macro_rules! get_attr {
     ($elem:ident, $attr:tt, $type:tt) => (
         get_attr!($elem, $attr, $type, value, value.parse()?)
     );
+    ($elem:ident, $attr:tt, optional_empty, $value:ident, $func:expr) => (
+        match $elem.attr($attr) {
+            Some("") => None,
+            Some($value) => Some($func),
+            None => None,
+        }
+    );
     ($elem:ident, $attr:tt, optional, $value:ident, $func:expr) => (
         match $elem.attr($attr) {
             Some($value) => Some($func),
@@ -277,6 +284,16 @@ macro_rules! generate_elem_id {
                 Ok($elem(String::from(s)))
             }
         }
+        impl TryFrom<Element> for $elem {
+            type Err = Error;
+            fn try_from(elem: Element) -> Result<$elem, Error> {
+                check_self!(elem, $name, $ns);
+                check_no_children!(elem, $name);
+                check_no_attributes!(elem, $name);
+                // TODO: add a way to parse that differently when needed.
+                Ok($elem(elem.text()))
+            }
+        }
         impl From<$elem> for Element {
             fn from(elem: $elem) -> Element {
                 Element::builder($name)

src/roster.rs 🔗

@@ -23,64 +23,26 @@ generate_attribute!(Subscription, "subscription", {
     Remove => "remove",
 }, Default = None);
 
-/// Contact from the user’s contact list.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Item {
-    /// JID of this contact.
-    pub jid: Jid,
-
-    /// Name of this contact.
-    pub name: Option<String>,
-
-    /// Subscription status of this contact.
-    pub subscription: Subscription,
-
-    /// Groups this contact is part of.
-    pub groups: Vec<Group>,
-}
-
-impl TryFrom<Element> for Item {
-    type Err = Error;
+generate_element_with_children!(
+    /// Contact from the user’s contact list.
+    #[derive(PartialEq)]
+    Item, "item", ns::ROSTER,
+    attributes: [
+        /// JID of this contact.
+        jid: Jid = "jid" => required,
 
-    fn try_from(elem: Element) -> Result<Item, Error> {
-        if !elem.is("item", ns::ROSTER) {
-            return Err(Error::ParseError("This is not a roster item element."));
-        }
+        /// Name of this contact.
+        name: Option<String> = "name" => optional_empty,
 
-        let mut item = Item {
-            jid: get_attr!(elem, "jid", required),
-            name: get_attr!(elem, "name", optional).and_then(|name| if name == "" { None } else { Some(name) }),
-            subscription: get_attr!(elem, "subscription", default),
-            groups: vec!(),
-        };
-        for child in elem.children() {
-            if !child.is("group", ns::ROSTER) {
-                return Err(Error::ParseError("Unknown element in roster item element."));
-            }
-            for _ in child.children() {
-                return Err(Error::ParseError("Roster item group can’t have children."));
-            }
-            for _ in child.attrs() {
-                return Err(Error::ParseError("Roster item group can’t have attributes."));
-            }
-            let group = Group(child.text());
-            item.groups.push(group);
-        }
-        Ok(item)
-    }
-}
+        /// Subscription status of this contact.
+        subscription: Subscription = "subscription" => default
+    ],
 
-impl From<Item> for Element {
-    fn from(item: Item) -> Element {
-        Element::builder("item")
-                .ns(ns::ROSTER)
-                .attr("jid", item.jid)
-                .attr("name", item.name)
-                .attr("subscription", item.subscription)
-                .append(item.groups)
-                .build()
-    }
-}
+    children: [
+        /// Groups this contact is part of.
+        groups: Vec<Group> = ("group", ns::ROSTER) => Group
+    ]
+);
 
 generate_element_with_children!(
     /// The contact list of the user.
@@ -258,6 +220,6 @@ mod tests {
             Error::ParseError(string) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown element in roster item element.");
+        assert_eq!(message, "Unknown child in item element.");
     }
 }