xmpp-parsers: Use xso for pubsub owner

Emmanuel Gil Peyrot created

Also merge its direct children into its enum fields.

Change summary

parsers/ChangeLog           |   2 
parsers/src/pubsub/mod.rs   |   2 
parsers/src/pubsub/owner.rs | 252 +++++++++++++++-----------------------
3 files changed, 102 insertions(+), 154 deletions(-)

Detailed changes

parsers/ChangeLog 🔗

@@ -62,6 +62,8 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
         having been exchanged, and the stream_id is now a String instead of
         &str.
       - Add a lang property in Presence, to avoid parser errors.
+      - pubsub::Owner is now a wrapper for the pubsub::owner::Paylolad enum,
+        and all its direct children have been merged into this enum (!532)
     * New parsers/serialisers:
       - Stream Features (RFC 6120) (!400)
       - Spam Reporting (XEP-0377) (!506)

parsers/src/pubsub/mod.rs 🔗

@@ -15,7 +15,7 @@ pub mod owner;
 pub mod pubsub;
 
 pub use self::event::Event;
-pub use self::owner::PubSubOwner;
+pub use self::owner::Owner;
 pub use self::pubsub::PubSub;
 
 use minidom::Element;

parsers/src/pubsub/owner.rs 🔗

@@ -12,21 +12,6 @@ use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 use crate::ns;
 use crate::pubsub::{AffiliationAttribute, NodeName, Subscription};
 use jid::Jid;
-use minidom::Element;
-use xso::error::{Error, FromElementError};
-
-/// A list of affiliations you have on a service, or on a node.
-#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "affiliations")]
-pub struct Affiliations {
-    /// The node name this request pertains to.
-    #[xml(attribute)]
-    pub node: NodeName,
-
-    /// The actual list of affiliation elements.
-    #[xml(child(n = ..))]
-    pub affiliations: Vec<Affiliation>,
-}
 
 /// An affiliation element.
 #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@@ -41,72 +26,6 @@ pub struct Affiliation {
     affiliation: AffiliationAttribute,
 }
 
-/// Request to configure a node.
-#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "configure")]
-pub struct Configure {
-    /// The node to be configured.
-    #[xml(attribute(default))]
-    pub node: Option<NodeName>,
-
-    /// The form to configure it.
-    #[xml(child(default))]
-    pub form: Option<DataForm>,
-}
-
-/// Request to retrieve default configuration.
-#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "default")]
-pub struct Default {
-    /// The form to configure it.
-    #[xml(child(default))]
-    pub form: Option<DataForm>,
-}
-
-/// Request to delete a node.
-#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "delete")]
-pub struct Delete {
-    /// The node to be deleted.
-    #[xml(attribute)]
-    pub node: NodeName,
-
-    /// Redirection to replace the deleted node.
-    #[xml(child(default))]
-    pub redirect: Option<Redirect>,
-}
-
-/// A redirect element.
-#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "redirect")]
-pub struct Redirect {
-    /// The node this node will be redirected to.
-    #[xml(attribute)]
-    pub uri: String,
-}
-
-/// Request to clear a node.
-#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "purge")]
-pub struct Purge {
-    /// The node to be cleared.
-    #[xml(attribute)]
-    pub node: NodeName,
-}
-
-/// A request for current subscriptions.
-#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
-#[xml(namespace = ns::PUBSUB_OWNER, name = "subscriptions")]
-pub struct Subscriptions {
-    /// The node to query.
-    #[xml(attribute)]
-    pub node: NodeName,
-
-    /// The list of subscription elements returned.
-    #[xml(child(n = ..))]
-    pub subscriptions: Vec<SubscriptionElem>,
-}
-
 /// A subscription element, describing the state of a subscription.
 #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
 #[xml(namespace = ns::PUBSUB_OWNER, name = "subscription")]
@@ -124,68 +43,89 @@ pub struct SubscriptionElem {
     pub subid: Option<String>,
 }
 
-/// Main payload used to communicate with a PubSubOwner service.
+/// Represents an owner request to a PubSub service.
+#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::PUBSUB_OWNER, name = "pubsub")]
+pub struct Owner {
+    /// The inner child of this request.
+    #[xml(child)]
+    pub payload: Payload,
+}
+
+// TODO: Differentiate each payload per type someday, to simplify our types.
+impl IqGetPayload for Owner {}
+impl IqSetPayload for Owner {}
+impl IqResultPayload for Owner {}
+
+/// Main payload used to communicate with a PubSub service.
 ///
 /// `<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"/>`
-#[derive(Debug, Clone, PartialEq)]
-pub enum PubSubOwner {
+#[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
+#[xml(namespace = ns::PUBSUB_OWNER, exhaustive)]
+pub enum Payload {
     /// Manage the affiliations of a node.
-    Affiliations(Affiliations),
-    /// Request to configure a node, with optional suggested name and suggested configuration.
-    Configure(Configure),
+    #[xml(name = "affiliations")]
+    Affiliations {
+        /// The node name this request pertains to.
+        #[xml(attribute)]
+        node: NodeName,
+
+        /// The actual list of affiliation elements.
+        #[xml(child(n = ..))]
+        affiliations: Vec<Affiliation>,
+    },
+
+    /// Request to configure a node.
+    #[xml(name = "configure")]
+    Configure {
+        /// The node to be configured.
+        #[xml(attribute(default))]
+        node: Option<NodeName>,
+
+        /// The form to configure it.
+        #[xml(child(default))]
+        form: Option<DataForm>,
+    },
+
     /// Request the default node configuration.
-    Default(Default),
+    #[xml(name = "default")]
+    Default {
+        /// The form to configure it.
+        #[xml(child(default))]
+        form: Option<DataForm>,
+    },
+
     /// Delete a node.
-    Delete(Delete),
-    /// Purge all items from node.
-    Purge(Purge),
-    /// Request subscriptions of a node.
-    Subscriptions(Subscriptions),
-}
+    #[xml(name = "delete")]
+    Delete {
+        /// The node to be deleted.
+        #[xml(attribute)]
+        node: NodeName,
 
-impl IqGetPayload for PubSubOwner {}
-impl IqSetPayload for PubSubOwner {}
-impl IqResultPayload for PubSubOwner {}
-
-impl TryFrom<Element> for PubSubOwner {
-    type Error = FromElementError;
-
-    fn try_from(elem: Element) -> Result<PubSubOwner, FromElementError> {
-        check_self!(elem, "pubsub", PUBSUB_OWNER);
-        check_no_attributes!(elem, "pubsub");
-
-        let mut payload = None;
-        for child in elem.children() {
-            if child.is("configure", ns::PUBSUB_OWNER) {
-                if payload.is_some() {
-                    return Err(Error::Other(
-                        "Payload is already defined in pubsub owner element.",
-                    )
-                    .into());
-                }
-                let configure = Configure::try_from(child.clone())?;
-                payload = Some(PubSubOwner::Configure(configure));
-            } else {
-                return Err(Error::Other("Unknown child in pubsub element.").into());
-            }
-        }
-        payload.ok_or(Error::Other("No payload in pubsub element.").into())
-    }
-}
+        /// Redirection to replace the deleted node.
+        #[xml(extract(default, name = "redirect", fields(attribute(name = "uri", type_ = String))))]
+        redirect_uri: Option<String>,
+    },
 
-impl From<PubSubOwner> for Element {
-    fn from(pubsub: PubSubOwner) -> Element {
-        Element::builder("pubsub", ns::PUBSUB_OWNER)
-            .append_all(match pubsub {
-                PubSubOwner::Affiliations(affiliations) => vec![Element::from(affiliations)],
-                PubSubOwner::Configure(configure) => vec![Element::from(configure)],
-                PubSubOwner::Default(default) => vec![Element::from(default)],
-                PubSubOwner::Delete(delete) => vec![Element::from(delete)],
-                PubSubOwner::Purge(purge) => vec![Element::from(purge)],
-                PubSubOwner::Subscriptions(subscriptions) => vec![Element::from(subscriptions)],
-            })
-            .build()
-    }
+    /// Purge all items from node.
+    #[xml(name = "purge")]
+    Purge {
+        /// The node to be cleared.
+        #[xml(attribute)]
+        node: NodeName,
+    },
+
+    /// Request the current subscriptions to a node.
+    #[xml(name = "subscriptions")]
+    Subscriptions {
+        /// The node to query.
+        #[xml(attribute)]
+        node: NodeName,
+
+        /// The list of subscription elements returned.
+        #[xml(child(n = ..))]
+        subscriptions: Vec<SubscriptionElem>,
+    },
 }
 
 #[cfg(test)]
@@ -194,6 +134,7 @@ mod tests {
     use crate::data_forms::{DataFormType, Field, FieldType};
     use core::str::FromStr;
     use jid::BareJid;
+    use minidom::Element;
 
     #[test]
     fn affiliations() {
@@ -202,7 +143,7 @@ mod tests {
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Affiliations(Affiliations {
+        let payload = Payload::Affiliations {
             node: NodeName(String::from("foo")),
             affiliations: vec![
                 Affiliation {
@@ -214,7 +155,8 @@ mod tests {
                     affiliation: AffiliationAttribute::Outcast,
                 },
             ],
-        });
+        };
+        let pubsub = Owner { payload };
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);
@@ -227,7 +169,7 @@ mod tests {
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Configure(Configure {
+        let payload = Payload::Configure {
             node: Some(NodeName(String::from("foo"))),
             form: Some(DataForm::new(
                 DataFormType::Submit,
@@ -235,7 +177,8 @@ mod tests {
                 vec![Field::new("pubsub#access_model", FieldType::ListSingle)
                     .with_value("whitelist")],
             )),
-        });
+        };
+        let pubsub = Owner { payload };
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);
@@ -251,10 +194,11 @@ mod tests {
 
         let form = DataForm::try_from(elem).unwrap();
 
-        let configure = PubSubOwner::Configure(Configure {
+        let payload = Payload::Configure {
             node: Some(NodeName(String::from("foo"))),
             form: Some(form),
-        });
+        };
+        let configure = Owner { payload };
         let serialized: Element = configure.into();
         assert_eq!(serialized, reference);
     }
@@ -266,14 +210,15 @@ mod tests {
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Default(Default {
+        let payload = Payload::Default {
             form: Some(DataForm::new(
                 DataFormType::Submit,
                 ns::PUBSUB_CONFIGURE,
                 vec![Field::new("pubsub#access_model", FieldType::ListSingle)
                     .with_value("whitelist")],
             )),
-        });
+        };
+        let pubsub = Owner { payload };
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);
@@ -286,12 +231,11 @@ mod tests {
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Delete(Delete {
+        let payload = Payload::Delete {
             node: NodeName(String::from("foo")),
-            redirect: Some(Redirect {
-                uri: String::from("xmpp:hamlet@denmark.lit?;node=blog"),
-            }),
-        });
+            redirect_uri: Some(String::from("xmpp:hamlet@denmark.lit?;node=blog")),
+        };
+        let pubsub = Owner { payload };
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);
@@ -304,9 +248,10 @@ mod tests {
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Purge(Purge {
+        let payload = Payload::Purge {
             node: NodeName(String::from("foo")),
-        });
+        };
+        let pubsub = Owner { payload };
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);
@@ -319,7 +264,7 @@ mod tests {
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Subscriptions(Subscriptions {
+        let payload = Payload::Subscriptions {
             node: NodeName(String::from("foo")),
             subscriptions: vec![
                 SubscriptionElem {
@@ -343,7 +288,8 @@ mod tests {
                     subid: Some(String::from("004-yyy")),
                 },
             ],
-        });
+        };
+        let pubsub = Owner { payload };
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);