@@ -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);