xmpp-parsers: Use xso for pubsub owner

Also merge its direct children into its enum fields.
This commit is contained in:
Emmanuel Gil Peyrot 2025-01-27 15:14:57 +01:00
commit 5cbe5936a6
3 changed files with 102 additions and 154 deletions

View file

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

View file

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

View file

@ -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),
#[xml(name = "delete")]
Delete {
/// The node to be deleted.
#[xml(attribute)]
node: NodeName,
/// Redirection to replace the deleted node.
#[xml(extract(default, name = "redirect", fields(attribute(name = "uri", type_ = String))))]
redirect_uri: Option<String>,
},
/// Purge all items from node.
Purge(Purge),
/// Request subscriptions of a node.
Subscriptions(Subscriptions),
}
#[xml(name = "purge")]
Purge {
/// The node to be cleared.
#[xml(attribute)]
node: NodeName,
},
impl IqGetPayload for PubSubOwner {}
impl IqSetPayload for PubSubOwner {}
impl IqResultPayload for PubSubOwner {}
/// Request the current subscriptions to a node.
#[xml(name = "subscriptions")]
Subscriptions {
/// The node to query.
#[xml(attribute)]
node: NodeName,
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()
}
/// 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);