xmpp-parsers: Convert roster item to xso

Emmanuel Gil Peyrot created

Change summary

parsers/src/roster.rs      | 72 ++++++++++++++++++++--------------------
parsers/src/util/macros.rs | 18 ++++++++++
2 files changed, 54 insertions(+), 36 deletions(-)

Detailed changes

parsers/src/roster.rs đź”—

@@ -48,28 +48,30 @@ generate_attribute!(
     )
 );
 
-generate_element!(
-    /// Contact from the user’s contact list.
-    Item, "item", ROSTER,
-    attributes: [
-        /// JID of this contact.
-        jid: Required<BareJid> = "jid",
-
-        /// Name of this contact.
-        name: OptionEmpty<String> = "name",
-
-        /// Subscription status of this contact.
-        subscription: Default<Subscription> = "subscription",
-
-        /// Indicates “Pending Out” sub-states for this contact.
-        ask: Default<Ask> = "ask",
-    ],
-
-    children: [
-        /// Groups this contact is part of.
-        groups: Vec<Group> = ("group", ROSTER) => Group
-    ]
-);
+/// Contact from the user’s contact list.
+#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::ROSTER, name = "item")]
+pub struct Item {
+    /// JID of this contact.
+    #[xml(attribute)]
+    pub jid: BareJid,
+
+    /// Name of this contact.
+    #[xml(attribute(default))]
+    pub name: Option<String>,
+
+    /// Subscription status of this contact.
+    #[xml(attribute(default))]
+    pub subscription: Subscription,
+
+    /// Indicates “Pending Out” sub-states for this contact.
+    #[xml(attribute(default))]
+    pub ask: Ask,
+
+    /// Groups this contact is part of.
+    #[xml(child(n = ..))]
+    pub groups: Vec<Group>,
+}
 
 /// The contact list of the user.
 #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@@ -134,10 +136,6 @@ mod tests {
         assert_eq!(roster.ver, Some(String::from("ver7")));
         assert_eq!(roster.items.len(), 2);
 
-        let elem2: Element = "<query xmlns='jabber:iq:roster' ver='ver7'><item jid='nurse@example.com'/><item jid='romeo@example.net' name=''/></query>".parse().unwrap();
-        let roster2 = Roster::try_from(elem2).unwrap();
-        assert_eq!(roster.items, roster2.items);
-
         let elem: Element = "<query xmlns='jabber:iq:roster' ver='ver9'/>"
             .parse()
             .unwrap();
@@ -300,17 +298,19 @@ mod tests {
             FromElementError::Invalid(Error::Other(string)) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Required attribute 'jid' missing.");
+        assert_eq!(
+            message,
+            "Required attribute field 'jid' on Item element missing."
+        );
 
-        /*
-        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid=''/></query>".parse().unwrap();
+        let elem: Element = "<query xmlns='jabber:iq:roster'><item jid=''/></query>"
+            .parse()
+            .unwrap();
         let error = Roster::try_from(elem).unwrap_err();
-        let error = match error {
-            Error::JidParseError(error) => error,
-            _ => panic!(),
-        };
-        assert_eq!(error.description(), "Invalid JID, I guess?");
-        */
+        assert_eq!(
+            format!("{error}"),
+            "text parse error: no domain found in this JID"
+        );
 
         let elem: Element =
             "<query xmlns='jabber:iq:roster'><item jid='coucou'><coucou/></item></query>"
@@ -321,6 +321,6 @@ mod tests {
             FromElementError::Invalid(Error::Other(string)) => string,
             _ => panic!(),
         };
-        assert_eq!(message, "Unknown child in item element.");
+        assert_eq!(message, "Unknown child in Item element.");
     }
 }

parsers/src/util/macros.rs đź”—

@@ -195,6 +195,24 @@ macro_rules! generate_attribute {
                 $elem::None
             }
         }
+        impl ::xso::FromXmlText for $elem {
+            fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> {
+                s.parse().map_err(xso::error::Error::text_parse_error)
+            }
+        }
+        impl ::xso::AsXmlText for $elem {
+            fn as_xml_text(&self) -> Result<::std::borrow::Cow<'_, str>, xso::error::Error> {
+                Ok(::std::borrow::Cow::Borrowed($value))
+            }
+
+            #[allow(unreachable_patterns)]
+            fn as_optional_xml_text(&self) -> Result<Option<std::borrow::Cow<'_, str>>, xso::error::Error> {
+                Ok(Some(std::borrow::Cow::Borrowed(match self {
+                    $elem::$symbol => $value,
+                    $elem::None => return Ok(None),
+                })))
+            }
+        }
     );
     ($(#[$meta:meta])* $elem:ident, $name:tt, bool) => (
         $(#[$meta])*