use xso::{AsXml, FromXml};
use crate::data_forms::DataForm;
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};
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "affiliations")]
pub struct Affiliations {
#[xml(attribute)]
pub node: NodeName,
#[xml(child(n = ..))]
pub affiliations: Vec<Affiliation>,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "affiliation")]
pub struct Affiliation {
#[xml(attribute)]
jid: Jid,
#[xml(attribute)]
affiliation: AffiliationAttribute,
}
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "configure")]
pub struct Configure {
#[xml(attribute(default))]
pub node: Option<NodeName>,
#[xml(child(default))]
pub form: Option<DataForm>,
}
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "default")]
pub struct Default {
#[xml(child(default))]
pub form: Option<DataForm>,
}
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "delete")]
pub struct Delete {
#[xml(attribute)]
pub node: NodeName,
#[xml(child(default))]
pub redirect: Option<Redirect>,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "redirect")]
pub struct Redirect {
#[xml(attribute)]
pub uri: String,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "purge")]
pub struct Purge {
#[xml(attribute)]
pub node: NodeName,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "subscriptions")]
pub struct Subscriptions {
#[xml(attribute)]
pub node: NodeName,
#[xml(child(n = ..))]
pub subscriptions: Vec<SubscriptionElem>,
}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "subscription")]
pub struct SubscriptionElem {
#[xml(attribute)]
pub jid: Jid,
#[xml(attribute)]
pub subscription: Subscription,
#[xml(attribute(default))]
pub subid: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PubSubOwner {
Affiliations(Affiliations),
Configure(Configure),
Default(Default),
Delete(Delete),
Purge(Purge),
Subscriptions(Subscriptions),
}
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())
}
}
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()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data_forms::{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::from(BareJid::from_str("hamlet@denmark.lit").unwrap()),
affiliation: AffiliationAttribute::Owner,
},
Affiliation {
jid: Jid::from(BareJid::from_str("polonius@denmark.lit").unwrap()),
affiliation: AffiliationAttribute::Outcast,
},
],
});
let elem2 = Element::from(pubsub);
assert_eq!(elem1, elem2);
}
#[test]
fn configure() {
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 {
node: Some(NodeName(String::from("foo"))),
form: Some(DataForm::new(
DataFormType::Submit,
ns::PUBSUB_CONFIGURE,
vec![Field::new("pubsub#access_model", FieldType::ListSingle)
.with_value("whitelist")],
)),
});
let elem2 = Element::from(pubsub);
assert_eq!(elem1, elem2);
}
#[test]
fn test_serialize_configure() {
let reference: Element = "<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'><configure node='foo'><x xmlns='jabber:x:data' type='submit'/></configure></pubsub>"
.parse()
.unwrap();
let elem: Element = "<x xmlns='jabber:x:data' type='submit'/>".parse().unwrap();
let form = DataForm::try_from(elem).unwrap();
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::new(
DataFormType::Submit,
ns::PUBSUB_CONFIGURE,
vec![Field::new("pubsub#access_model", FieldType::ListSingle)
.with_value("whitelist")],
)),
});
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: Some(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::from(BareJid::from_str("hamlet@denmark.lit").unwrap()),
subscription: Subscription::Subscribed,
subid: None,
},
SubscriptionElem {
jid: Jid::from(BareJid::from_str("polonius@denmark.lit").unwrap()),
subscription: Subscription::Unconfigured,
subid: None,
},
SubscriptionElem {
jid: Jid::from(BareJid::from_str("bernardo@denmark.lit").unwrap()),
subscription: Subscription::Subscribed,
subid: Some(String::from("123-abc")),
},
SubscriptionElem {
jid: Jid::from(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);
}
}