Implement all sub element of pubsub#owner

Paul Fariello created

Change summary

xmpp-parsers/src/pubsub/mod.rs    |  23 ++
xmpp-parsers/src/pubsub/owner.rs  | 296 ++++++++++++++++++++++++++++----
xmpp-parsers/src/pubsub/pubsub.rs |  27 --
3 files changed, 286 insertions(+), 60 deletions(-)

Detailed changes

xmpp-parsers/src/pubsub/mod.rs πŸ”—

@@ -52,6 +52,29 @@ generate_attribute!(
     }, Default = None
 );
 
+generate_attribute!(
+    /// A list of possible affiliations to a node.
+    AffiliationAttribute, "affiliation", {
+        /// You are a member of this node, you can subscribe and retrieve items.
+        Member => "member",
+
+        /// You don’t have a specific affiliation with this node, you can only subscribe to it.
+        None => "none",
+
+        /// You are banned from this node.
+        Outcast => "outcast",
+
+        /// You are an owner of this node, and can do anything with it.
+        Owner => "owner",
+
+        /// You are a publisher on this node, you can publish and retract items to it.
+        Publisher => "publisher",
+
+        /// You can publish and retract items on this node, but not subscribe or retrieve items.
+        PublishOnly => "publish-only",
+    }
+);
+
 /// An item from a PubSub node.
 #[derive(Debug, Clone, PartialEq)]
 pub struct Item {

xmpp-parsers/src/pubsub/owner.rs πŸ”—

@@ -7,11 +7,37 @@
 use crate::data_forms::DataForm;
 use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 use crate::ns;
-use crate::pubsub::NodeName;
+use crate::pubsub::{AffiliationAttribute, NodeName, Subscription};
 use crate::util::error::Error;
 use crate::Element;
+use jid::Jid;
 use std::convert::TryFrom;
 
+generate_element!(
+    /// A list of affiliations you have on a service, or on a node.
+    Affiliations, "affiliations", PUBSUB_OWNER,
+    attributes: [
+        /// The node name this request pertains to.
+        node: Required<NodeName> = "node",
+    ],
+    children: [
+        /// The actual list of affiliation elements.
+        affiliations: Vec<Affiliation> = ("affiliation", PUBSUB_OWNER) => Affiliation
+    ]
+);
+
+generate_element!(
+    /// An affiliation element.
+    Affiliation, "affiliation", PUBSUB_OWNER,
+    attributes: [
+        /// The node this affiliation pertains to.
+        jid: Required<Jid> = "jid",
+
+        /// The affiliation you currently have on this node.
+        affiliation: Required<AffiliationAttribute> = "affiliation",
+    ]
+);
+
 generate_element!(
     /// Request to configure a node.
     Configure, "configure", PUBSUB_OWNER,
@@ -25,16 +51,91 @@ generate_element!(
     ]
 );
 
+generate_element!(
+    /// Request to change default configuration.
+    Default, "default", PUBSUB_OWNER,
+    children: [
+        /// The form to configure it.
+        form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
+    ]
+);
+
+generate_element!(
+    /// Request to delete a node.
+    Delete, "delete", PUBSUB_OWNER,
+    attributes: [
+        /// The node to be configured.
+        node: Required<NodeName> = "node",
+    ],
+    children: [
+        /// Redirection to replace the deleted node.
+        redirect: Vec<Redirect> = ("redirect", PUBSUB_OWNER) => Redirect
+    ]
+);
+
+generate_element!(
+    /// A redirect element.
+    Redirect, "redirect", PUBSUB_OWNER,
+    attributes: [
+        /// The node this node will be redirected to.
+        uri: Required<String> = "uri",
+    ]
+);
+
+generate_element!(
+    /// Request to delete a node.
+    Purge, "purge", PUBSUB_OWNER,
+    attributes: [
+        /// The node to be configured.
+        node: Required<NodeName> = "node",
+    ]
+);
+
+generate_element!(
+    /// A request for current subscriptions.
+    Subscriptions, "subscriptions", PUBSUB_OWNER,
+    attributes: [
+        /// The node to query.
+        node: Required<NodeName> = "node",
+    ],
+    children: [
+        /// The list of subscription elements returned.
+        subscriptions: Vec<SubscriptionElem> = ("subscription", PUBSUB_OWNER) => SubscriptionElem
+    ]
+);
+
+generate_element!(
+    /// A subscription element, describing the state of a subscription.
+    SubscriptionElem, "subscription", PUBSUB_OWNER,
+    attributes: [
+        /// The JID affected by this subscription.
+        jid: Required<Jid> = "jid",
+
+        /// The state of the subscription.
+        subscription: Required<Subscription> = "subscription",
+
+        /// Subscription unique id.
+        subid: Option<String> = "subid",
+    ]
+);
+
 /// Main payload used to communicate with a PubSubOwner service.
 ///
 /// `<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"/>`
 #[derive(Debug, Clone)]
 pub enum PubSubOwner {
+    /// Manage the affiliations of a node.
+    Affiliations(Affiliations),
     /// Request to configure a node, with optional suggested name and suggested configuration.
-    Configure {
-        /// The configure request for the new node.
-        configure: Configure,
-    },
+    Configure(Configure),
+    /// Request the default node configuration.
+    Default(Default),
+    /// Delete a node.
+    Delete(Delete),
+    /// Purge all items from node.
+    Purge(Purge),
+    /// Request subscriptions of a node.
+    Subscriptions(Subscriptions),
 }
 
 impl IqGetPayload for PubSubOwner {}
@@ -57,9 +158,7 @@ impl TryFrom<Element> for PubSubOwner {
                     ));
                 }
                 let configure = Configure::try_from(child.clone())?;
-                payload = Some(PubSubOwner::Configure {
-                    configure: configure,
-                });
+                payload = Some(PubSubOwner::Configure(configure));
             } else {
                 return Err(Error::ParseError("Unknown child in pubsub element."));
             }
@@ -72,7 +171,12 @@ impl From<PubSubOwner> for Element {
     fn from(pubsub: PubSubOwner) -> Element {
         Element::builder("pubsub", ns::PUBSUB_OWNER)
             .append_all(match pubsub {
-                PubSubOwner::Configure { configure } => vec![Element::from(configure)],
+                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()
     }
@@ -82,36 +186,59 @@ impl From<PubSubOwner> for Element {
 mod tests {
     use super::*;
     use crate::data_forms::{DataForm, DataFormType, Field, FieldType};
+    use jid::BareJid;
     use std::str::FromStr;
 
+    #[test]
+    fn affiliations() {
+        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><affiliations node='foo'><affiliation jid='hamlet@denmark.lit' affiliation='owner'/><affiliation jid='polonius@denmark.lit' affiliation='outcast'/></affiliations></pubsub>"
+        .parse()
+        .unwrap();
+        let elem1 = elem.clone();
+
+        let pubsub = PubSubOwner::Affiliations(Affiliations {
+            node: NodeName(String::from("foo")),
+            affiliations: vec![
+                Affiliation {
+                    jid: Jid::Bare(BareJid::from_str("hamlet@denmark.lit").unwrap()),
+                    affiliation: AffiliationAttribute::Owner,
+                },
+                Affiliation {
+                    jid: Jid::Bare(BareJid::from_str("polonius@denmark.lit").unwrap()),
+                    affiliation: AffiliationAttribute::Outcast,
+                },
+            ],
+        });
+
+        let elem2 = Element::from(pubsub);
+        assert_eq!(elem1, elem2);
+    }
+
     #[test]
     fn configure() {
-        // XXX: Do we want xmpp-parsers to always specify the field type in the output Element?
         let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><configure node='foo'><x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var='pubsub#access_model' type='list-single'><value>whitelist</value></field></x></configure></pubsub>"
         .parse()
         .unwrap();
         let elem1 = elem.clone();
 
-        let pubsub = PubSubOwner::Configure {
-            configure: Configure {
-                node: Some(NodeName(String::from("foo"))),
-                form: Some(DataForm {
-                    type_: DataFormType::Submit,
-                    form_type: Some(String::from(ns::PUBSUB_CONFIGURE)),
-                    title: None,
-                    instructions: None,
-                    fields: vec![Field {
-                        var: String::from("pubsub#access_model"),
-                        type_: FieldType::ListSingle,
-                        label: None,
-                        required: false,
-                        options: vec![],
-                        values: vec![String::from("whitelist")],
-                        media: vec![],
-                    }],
-                }),
-            },
-        };
+        let pubsub = PubSubOwner::Configure(Configure {
+            node: Some(NodeName(String::from("foo"))),
+            form: Some(DataForm {
+                type_: DataFormType::Submit,
+                form_type: Some(String::from(ns::PUBSUB_CONFIGURE)),
+                title: None,
+                instructions: None,
+                fields: vec![Field {
+                    var: String::from("pubsub#access_model"),
+                    type_: FieldType::ListSingle,
+                    label: None,
+                    required: false,
+                    options: vec![],
+                    values: vec![String::from("whitelist")],
+                    media: vec![],
+                }],
+            }),
+        });
 
         let elem2 = Element::from(pubsub);
         assert_eq!(elem1, elem2);
@@ -127,13 +254,110 @@ mod tests {
 
         let form = DataForm::try_from(elem).unwrap();
 
-        let configure = PubSubOwner::Configure {
-            configure: Configure {
-                node: Some(NodeName(String::from("foo"))),
-                form: Some(form),
-            },
-        };
+        let configure = PubSubOwner::Configure(Configure {
+            node: Some(NodeName(String::from("foo"))),
+            form: Some(form),
+        });
         let serialized: Element = configure.into();
         assert_eq!(serialized, reference);
     }
+
+    #[test]
+    fn default() {
+        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><default><x xmlns='jabber:x:data' type='submit'><field var='FORM_TYPE' type='hidden'><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var='pubsub#access_model' type='list-single'><value>whitelist</value></field></x></default></pubsub>"
+        .parse()
+        .unwrap();
+        let elem1 = elem.clone();
+
+        let pubsub = PubSubOwner::Default(Default {
+            form: Some(DataForm {
+                type_: DataFormType::Submit,
+                form_type: Some(String::from(ns::PUBSUB_CONFIGURE)),
+                title: None,
+                instructions: None,
+                fields: vec![Field {
+                    var: String::from("pubsub#access_model"),
+                    type_: FieldType::ListSingle,
+                    label: None,
+                    required: false,
+                    options: vec![],
+                    values: vec![String::from("whitelist")],
+                    media: vec![],
+                }],
+            }),
+        });
+
+        let elem2 = Element::from(pubsub);
+        assert_eq!(elem1, elem2);
+    }
+
+    #[test]
+    fn delete() {
+        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><delete node='foo'><redirect uri='xmpp:hamlet@denmark.lit?;node=blog'/></delete></pubsub>"
+        .parse()
+        .unwrap();
+        let elem1 = elem.clone();
+
+        let pubsub = PubSubOwner::Delete(Delete {
+            node: NodeName(String::from("foo")),
+            redirect: vec![Redirect {
+                uri: String::from("xmpp:hamlet@denmark.lit?;node=blog"),
+            }],
+        });
+
+        let elem2 = Element::from(pubsub);
+        assert_eq!(elem1, elem2);
+    }
+
+    #[test]
+    fn purge() {
+        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><purge node='foo'></purge></pubsub>"
+        .parse()
+        .unwrap();
+        let elem1 = elem.clone();
+
+        let pubsub = PubSubOwner::Purge(Purge {
+            node: NodeName(String::from("foo")),
+        });
+
+        let elem2 = Element::from(pubsub);
+        assert_eq!(elem1, elem2);
+    }
+
+    #[test]
+    fn subscriptions() {
+        let elem: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><subscriptions node='foo'><subscription jid='hamlet@denmark.lit' subscription='subscribed'/><subscription jid='polonius@denmark.lit' subscription='unconfigured'/><subscription jid='bernardo@denmark.lit' subscription='subscribed' subid='123-abc'/><subscription jid='bernardo@denmark.lit' subscription='subscribed' subid='004-yyy'/></subscriptions></pubsub>"
+        .parse()
+        .unwrap();
+        let elem1 = elem.clone();
+
+        let pubsub = PubSubOwner::Subscriptions(Subscriptions {
+            node: NodeName(String::from("foo")),
+            subscriptions: vec![
+                SubscriptionElem {
+                    jid: Jid::Bare(BareJid::from_str("hamlet@denmark.lit").unwrap()),
+                    subscription: Subscription::Subscribed,
+                    subid: None,
+                },
+                SubscriptionElem {
+                    jid: Jid::Bare(BareJid::from_str("polonius@denmark.lit").unwrap()),
+                    subscription: Subscription::Unconfigured,
+                    subid: None,
+                },
+                SubscriptionElem {
+                    jid: Jid::Bare(BareJid::from_str("bernardo@denmark.lit").unwrap()),
+                    subscription: Subscription::Subscribed,
+                    subid: Some(String::from("123-abc")),
+                },
+                SubscriptionElem {
+                    jid: Jid::Bare(BareJid::from_str("bernardo@denmark.lit").unwrap()),
+                    subscription: Subscription::Subscribed,
+                    subid: Some(String::from("004-yyy")),
+                },
+            ],
+        });
+
+        let elem2 = Element::from(pubsub);
+        assert_eq!(elem1, elem2);
+    }
 }

xmpp-parsers/src/pubsub/pubsub.rs πŸ”—

@@ -7,7 +7,9 @@
 use crate::data_forms::DataForm;
 use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 use crate::ns;
-use crate::pubsub::{Item as PubSubItem, NodeName, Subscription, SubscriptionId};
+use crate::pubsub::{
+    AffiliationAttribute, Item as PubSubItem, NodeName, Subscription, SubscriptionId,
+};
 use crate::util::error::Error;
 use crate::Element;
 use jid::Jid;
@@ -28,29 +30,6 @@ generate_element!(
     ]
 );
 
-generate_attribute!(
-    /// A list of possible affiliations to a node.
-    AffiliationAttribute, "affiliation", {
-        /// You are a member of this node, you can subscribe and retrieve items.
-        Member => "member",
-
-        /// You don’t have a specific affiliation with this node, you can only subscribe to it.
-        None => "none",
-
-        /// You are banned from this node.
-        Outcast => "outcast",
-
-        /// You are an owner of this node, and can do anything with it.
-        Owner => "owner",
-
-        /// You are a publisher on this node, you can publish and retract items to it.
-        Publisher => "publisher",
-
-        /// You can publish and retract items on this node, but not subscribe or retrieve items.
-        PublishOnly => "publish-only",
-    }
-);
-
 generate_element!(
     /// An affiliation element.
     Affiliation, "affiliation", PUBSUB,