parsers: port more things to derive macros

This commit is contained in:
Jonas Schäfer 2024-08-03 13:04:56 +02:00
commit 2b346c4e87
18 changed files with 382 additions and 304 deletions

View file

@ -13,14 +13,14 @@ use crate::hashes::Sha1HexAttribute;
use crate::ns; use crate::ns;
use crate::pubsub::PubSubPayload; use crate::pubsub::PubSubPayload;
generate_element!( /// Communicates information about an avatar.
/// Communicates information about an avatar. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Metadata, "metadata", AVATAR_METADATA, #[xml(namespace = ns::AVATAR_METADATA, name = "metadata")]
children: [ pub struct Metadata {
/// List of information elements describing this avatar. /// List of information elements describing this avatar.
infos: Vec<Info> = ("info", AVATAR_METADATA) => Info #[xml(child(n = ..))]
] pub infos: Vec<Info>,
); }
impl PubSubPayload for Metadata {} impl PubSubPayload for Metadata {}

View file

@ -56,14 +56,14 @@ generate_elem_id!(
SASL_CERT SASL_CERT
); );
generate_element!( /// A list of resources currently using this certificate.
/// A list of resources currently using this certificate. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Users, "users", SASL_CERT, #[xml(namespace = ns::SASL_CERT, name = "users")]
children: [ pub struct Users {
/// Resources currently using this certificate. /// Resources currently using this certificate.
resources: Vec<Resource> = ("resource", SASL_CERT) => Resource #[xml(child(n = ..))]
] pub resources: Vec<Resource>,
); }
generate_element!( generate_element!(
/// An X.509 certificate being set for this user. /// An X.509 certificate being set for this user.
@ -83,14 +83,14 @@ generate_element!(
] ]
); );
generate_element!( /// Server answers with the current list of X.509 certificates.
/// Server answers with the current list of X.509 certificates. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
ListCertsResponse, "items", SASL_CERT, #[xml(namespace = ns::SASL_CERT, name = "items")]
children: [ pub struct ListCertsResponse {
/// List of certificates. /// List of certificates.
items: Vec<Item> = ("item", SASL_CERT) => Item #[xml(child(n = ..))]
] pub items: Vec<Item>,
); }
impl IqResultPayload for ListCertsResponse {} impl IqResultPayload for ListCertsResponse {}

View file

@ -220,25 +220,26 @@ pub struct Item {
pub name: Option<String>, pub name: Option<String>,
} }
generate_element!( /// Structure representing a `<query
/// Structure representing a `<query /// xmlns='http://jabber.org/protocol/disco#items'/>` element.
/// xmlns='http://jabber.org/protocol/disco#items'/>` element. ///
/// /// It should only be used in an `<iq type='result'/>`, as it can only
/// It should only be used in an `<iq type='result'/>`, as it can only /// represent the result, and not a request.
/// represent the result, and not a request. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
DiscoItemsResult, "query", DISCO_ITEMS, #[xml(namespace = ns::DISCO_ITEMS, name = "query")]
attributes: [ pub struct DiscoItemsResult {
/// Node on which we have done this discovery. /// Node on which we have done this discovery.
node: Option<String> = "node" #[xml(attribute(default))]
], pub node: Option<String>,
children: [
/// List of items pointed by this entity.
items: Vec<Item> = ("item", DISCO_ITEMS) => Item,
/// Optional paging via Result Set Management /// List of items pointed by this entity.
rsm: Option<crate::rsm::SetResult> = ("set", RSM) => SetResult, #[xml(child(n = ..))]
] pub items: Vec<Item>,
);
/// Optional paging via Result Set Management
#[xml(child(default))]
pub rsm: Option<SetResult>,
}
impl IqResultPayload for DiscoItemsResult {} impl IqResultPayload for DiscoItemsResult {}

View file

@ -4,6 +4,8 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{AsXml, FromXml};
use crate::data_forms::DataForm; use crate::data_forms::DataForm;
use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity}; use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity};
use crate::hashes::{Algo, Hash}; use crate::hashes::{Algo, Hash};
@ -16,16 +18,16 @@ use sha2::{Sha256, Sha512};
use sha3::{Sha3_256, Sha3_512}; use sha3::{Sha3_256, Sha3_512};
use xso::error::Error; use xso::error::Error;
generate_element!( /// Represents a set of capability hashes, all of them must correspond to
/// Represents a set of capability hashes, all of them must correspond to /// the same input [disco#info](../disco/struct.DiscoInfoResult.html),
/// the same input [disco#info](../disco/struct.DiscoInfoResult.html), /// using different [algorithms](../hashes/enum.Algo.html).
/// using different [algorithms](../hashes/enum.Algo.html). #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
ECaps2, "c", ECAPS2, #[xml(namespace = ns::ECAPS2, name = "c")]
children: [ pub struct ECaps2 {
/// Hashes of the [disco#info](../disco/struct.DiscoInfoResult.html). /// Hashes of the [disco#info](../disco/struct.DiscoInfoResult.html).
hashes: Vec<Hash> = ("hash", HASHES) => Hash #[xml(child(n = ..))]
] pub hashes: Vec<Hash>,
); }
impl PresencePayload for ECaps2 {} impl PresencePayload for ECaps2 {}
@ -230,7 +232,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in c element."); assert_eq!(message, "Unknown child in ECaps2 element.");
} }
#[test] #[test]

View file

@ -104,37 +104,37 @@ impl IqGetPayload for Service {}
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::EXT_DISCO, name = "services")] #[xml(namespace = ns::EXT_DISCO, name = "services")]
pub struct ServicesQuery { pub struct ServicesQuery {
/// TODO /// The type of service to filter for.
#[xml(attribute(default, name = "type"))] #[xml(attribute(default, name = "type"))]
pub type_: Option<Type>, pub type_: Option<Type>,
} }
impl IqGetPayload for ServicesQuery {} impl IqGetPayload for ServicesQuery {}
generate_element!( /// Structure representing a `<services xmlns='urn:xmpp:extdisco:2'/>` element.
/// Structure representing a `<services xmlns='urn:xmpp:extdisco:2'/>` element. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
ServicesResult, "services", EXT_DISCO, #[xml(namespace = ns::EXT_DISCO, name = "services")]
attributes: [ pub struct ServicesResult {
/// TODO /// The service type which was requested.
type_: Option<Type> = "type", #[xml(attribute(name = "type", default))]
], pub type_: Option<Type>,
children: [
/// List of services. /// List of services.
services: Vec<Service> = ("service", EXT_DISCO) => Service #[xml(child(n = ..))]
] pub services: Vec<Service>,
); }
impl IqResultPayload for ServicesResult {} impl IqResultPayload for ServicesResult {}
impl IqSetPayload for ServicesResult {} impl IqSetPayload for ServicesResult {}
generate_element!( /// Structure representing a `<credentials xmlns='urn:xmpp:extdisco:2'/>` element.
/// Structure representing a `<credentials xmlns='urn:xmpp:extdisco:2'/>` element. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Credentials, "credentials", EXT_DISCO, #[xml(namespace = ns::EXT_DISCO, name = "credentials")]
children: [ pub struct Credentials {
/// List of services. /// List of services.
services: Vec<Service> = ("service", EXT_DISCO) => Service #[xml(child(n = ..))]
] pub services: Vec<Service>,
); }
impl IqGetPayload for Credentials {} impl IqGetPayload for Credentials {}
impl IqResultPayload for Credentials {} impl IqResultPayload for Credentials {}

View file

@ -5,8 +5,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{ use xso::{
error::{Error, FromElementError}, error::{Error, FromElementError, FromEventsError},
AsXml, FromXml, exports::rxml,
minidom_compat, AsXml, FromXml,
}; };
use crate::iq::{IqGetPayload, IqResultPayload}; use crate::iq::{IqGetPayload, IqResultPayload};
@ -68,6 +69,20 @@ impl TryFrom<Element> for Header {
} }
} }
impl FromXml for Header {
type Builder = minidom_compat::FromEventsViaElement<Header>;
fn from_events(
qname: rxml::QName,
attrs: rxml::AttrMap,
) -> Result<Self::Builder, FromEventsError> {
if qname.0 != ns::HTTP_UPLOAD || qname.1 != "header" {
return Err(FromEventsError::Mismatch { name: qname, attrs });
}
Self::Builder::new(qname, attrs)
}
}
impl From<Header> for Element { impl From<Header> for Element {
fn from(elem: Header) -> Element { fn from(elem: Header) -> Element {
let (attr, val) = match elem { let (attr, val) = match elem {
@ -83,18 +98,26 @@ impl From<Header> for Element {
} }
} }
generate_element!( impl AsXml for Header {
/// Put URL type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
Put, "put", HTTP_UPLOAD,
attributes: [ fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
/// URL minidom_compat::AsItemsViaElement::new(self.clone())
url: Required<String> = "url", }
], }
children: [
/// Header list /// Put URL
headers: Vec<Header> = ("header", HTTP_UPLOAD) => Header #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
] #[xml(namespace = ns::HTTP_UPLOAD, name = "put")]
); pub struct Put {
/// URL
#[xml(attribute)]
pub url: String,
/// Header list
#[xml(child(n = ..))]
pub headers: Vec<Header>,
}
/// Get URL /// Get URL
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]

View file

@ -38,18 +38,18 @@ impl Content {
} }
} }
generate_element!( /// A semantic group of contents.
/// A semantic group of contents. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Group, "group", JINGLE_GROUPING, #[xml(namespace = ns::JINGLE_GROUPING, name = "group")]
attributes: [ pub struct Group {
/// Semantics of the grouping. /// Semantics of the grouping.
semantics: Required<Semantics> = "semantics", #[xml(attribute)]
], pub semantics: Semantics,
children: [
/// List of contents that should be grouped with each other. /// List of contents that should be grouped with each other.
contents: Vec<Content> = ("content", JINGLE_GROUPING) => Content #[xml(child(n = ..))]
] pub contents: Vec<Content>,
); }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -8,18 +8,18 @@ use xso::{AsXml, FromXml};
use crate::ns; use crate::ns;
generate_element!( /// Source element for the ssrc SDP attribute.
/// Source element for the ssrc SDP attribute. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Source, "source", JINGLE_SSMA, #[xml(namespace = ns::JINGLE_SSMA, name = "source")]
attributes: [ pub struct Source {
/// Maps to the ssrc-id parameter. /// Maps to the ssrc-id parameter.
id: Required<u32> = "ssrc", #[xml(attribute = "ssrc")]
], pub id: u32,
children: [
/// List of attributes for this source. /// List of attributes for this source.
parameters: Vec<Parameter> = ("parameter", JINGLE_SSMA) => Parameter #[xml(child(n = ..))]
] pub parameters: Vec<Parameter>,
); }
impl Source { impl Source {
/// Create a new SSMA Source element. /// Create a new SSMA Source element.
@ -67,18 +67,18 @@ generate_attribute!(
} }
); );
generate_element!( /// Element grouping multiple ssrc.
/// Element grouping multiple ssrc. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Group, "ssrc-group", JINGLE_SSMA, #[xml(namespace = ns::JINGLE_SSMA, name = "ssrc-group")]
attributes: [ pub struct Group {
/// The semantics of this group. /// The semantics of this group.
semantics: Required<Semantics> = "semantics", #[xml(attribute)]
], pub semantics: Semantics,
children: [
/// The various ssrc concerned by this group. /// The various ssrc concerned by this group.
sources: Vec<Source> = ("source", JINGLE_SSMA) => Source #[xml(child(n = ..))]
] pub sources: Vec<Source>,
); }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -19,15 +19,15 @@ pub struct Device {
pub id: u32, pub id: u32,
} }
generate_element!( /// A user's device list contains the OMEMO device ids of all the user's
/// A user's device list contains the OMEMO device ids of all the user's /// devicse. These can be used to look up bundles and build a session.
/// devicse. These can be used to look up bundles and build a session. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
DeviceList, "list", LEGACY_OMEMO, #[xml(namespace = ns::LEGACY_OMEMO, name = "list")]
children: [ pub struct DeviceList {
/// List of devices /// List of devices
devices: Vec<Device> = ("device", LEGACY_OMEMO) => Device #[xml(child(n = ..))]
] pub devices: Vec<Device>,
); }
impl PubSubPayload for DeviceList {} impl PubSubPayload for DeviceList {}
@ -64,15 +64,15 @@ pub struct IdentityKey {
pub data: Vec<u8>, pub data: Vec<u8>,
} }
generate_element!( /// List of (single use) PreKeys
/// Part of a device's bundle
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::LEGACY_OMEMO, name = "prekeys")]
pub struct Prekeys {
/// List of (single use) PreKeys /// List of (single use) PreKeys
/// Part of a device's bundle #[xml(child(n = ..))]
Prekeys, "prekeys", LEGACY_OMEMO, pub keys: Vec<PreKeyPublic>,
children: [ }
/// List of (single use) PreKeys
keys: Vec<PreKeyPublic> = ("preKeyPublic", LEGACY_OMEMO) => PreKeyPublic,
]
);
/// PreKey public key /// PreKey public key
/// Part of a device's bundle /// Part of a device's bundle
@ -113,22 +113,23 @@ pub struct Bundle {
impl PubSubPayload for Bundle {} impl PubSubPayload for Bundle {}
generate_element!( /// The header contains encrypted keys for a message
/// The header contains encrypted keys for a message #[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
Header, "header", LEGACY_OMEMO, #[xml(namespace = ns::LEGACY_OMEMO, name = "header")]
attributes: [ pub struct Header {
/// The device id of the sender /// The device id of the sender
sid: Required<u32> = "sid", #[xml(attribute)]
], pub sid: u32,
children: [
/// The key that the payload message is encrypted with, separately
/// encrypted for each recipient device.
keys: Vec<Key> = ("key", LEGACY_OMEMO) => Key,
/// IV used for payload encryption /// The key that the payload message is encrypted with, separately
iv: Required<IV> = ("iv", LEGACY_OMEMO) => IV /// encrypted for each recipient device.
] #[xml(child(n = ..))]
); pub keys: Vec<Key>,
/// IV used for payload encryption
#[xml(child)]
pub iv: IV,
}
/// IV used for payload encryption /// IV used for payload encryption
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]

View file

@ -27,22 +27,23 @@ pub struct Uri {
pub uri: String, pub uri: String,
} }
generate_element!( /// References a media element, to be used in [data
/// References a media element, to be used in [data /// forms](../data_forms/index.html).
/// forms](../data_forms/index.html). #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
MediaElement, "media", MEDIA_ELEMENT, #[xml(namespace = ns::MEDIA_ELEMENT, name = "media")]
attributes: [ pub struct MediaElement {
/// The recommended display width in pixels. /// The recommended display width in pixels.
width: Option<usize> = "width", #[xml(attribute(default))]
pub width: Option<usize>,
/// The recommended display height in pixels. /// The recommended display height in pixels.
height: Option<usize> = "height" #[xml(attribute(default))]
], pub height: Option<usize>,
children: [
/// A list of URIs referencing this media. /// A list of URIs referencing this media.
uris: Vec<Uri> = ("uri", MEDIA_ELEMENT) => Uri #[xml(child(n = ..))]
] pub uris: Vec<Uri>,
); }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -162,7 +163,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in media element."); assert_eq!(message, "Unknown child in MediaElement element.");
} }
#[test] #[test]

View file

@ -113,21 +113,21 @@ impl Join {
} }
} }
generate_element!( /// Update a given subscription.
/// Update a given subscription. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
UpdateSubscription, "update-subscription", MIX_CORE, #[xml(namespace = ns::MIX_CORE, name = "update-subscription")]
attributes: [ pub struct UpdateSubscription {
/// The JID of the user to be affected. /// The JID of the user to be affected.
// TODO: why is it not a participant id instead? // TODO: why is it not a participant id instead?
jid: Option<BareJid> = "jid", #[xml(attribute(default))]
], pub jid: Option<BareJid>,
children: [
/// The list of additional nodes to subscribe to. /// The list of additional nodes to subscribe to.
// TODO: what happens when we are already subscribed? Also, how do we unsubscribe from // TODO: what happens when we are already subscribed? Also, how do we unsubscribe from
// just one? // just one?
subscribes: Vec<Subscribe> = ("subscribe", MIX_CORE) => Subscribe #[xml(child(n = ..))]
] pub subscribes: Vec<Subscribe>,
); }
impl IqSetPayload for UpdateSubscription {} impl IqSetPayload for UpdateSubscription {}
impl IqResultPayload for UpdateSubscription {} impl IqResultPayload for UpdateSubscription {}

View file

@ -48,14 +48,14 @@ pub struct PubKeyMeta {
pub date: DateTime, pub date: DateTime,
} }
generate_element!( /// List of public key metadata
/// List of public key metadata #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
PubKeysMeta, "public-key-list", OX, #[xml(namespace = ns::OX, name = "public-key-list")]
children: [ pub struct PubKeysMeta {
/// Public keys /// Public keys
pubkeys: Vec<PubKeyMeta> = ("pubkey-metadata", OX) => PubKeyMeta #[xml(child(n = ..))]
] pub pubkeys: Vec<PubKeyMeta>,
); }
impl PubSubPayload for PubKeysMeta {} impl PubSubPayload for PubKeysMeta {}

View file

@ -15,18 +15,18 @@ use jid::Jid;
use minidom::Element; use minidom::Element;
use xso::error::{Error, FromElementError}; use xso::error::{Error, FromElementError};
generate_element!( /// A list of affiliations you have on a service, or on a node.
/// A list of affiliations you have on a service, or on a node. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Affiliations, "affiliations", PUBSUB_OWNER, #[xml(namespace = ns::PUBSUB_OWNER, name = "affiliations")]
attributes: [ pub struct Affiliations {
/// The node name this request pertains to. /// The node name this request pertains to.
node: Required<NodeName> = "node", #[xml(attribute)]
], pub node: NodeName,
children: [
/// The actual list of affiliation elements. /// The actual list of affiliation elements.
affiliations: Vec<Affiliation> = ("affiliation", PUBSUB_OWNER) => Affiliation #[xml(child(n = ..))]
] pub affiliations: Vec<Affiliation>,
); }
/// An affiliation element. /// An affiliation element.
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@ -94,18 +94,18 @@ pub struct Purge {
pub node: NodeName, pub node: NodeName,
} }
generate_element!( /// A request for current subscriptions.
/// A request for current subscriptions. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Subscriptions, "subscriptions", PUBSUB_OWNER, #[xml(namespace = ns::PUBSUB_OWNER, name = "subscriptions")]
attributes: [ pub struct Subscriptions {
/// The node to query. /// The node to query.
node: Required<NodeName> = "node", #[xml(attribute)]
], pub node: NodeName,
children: [
/// The list of subscription elements returned. /// The list of subscription elements returned.
subscriptions: Vec<SubscriptionElem> = ("subscription", PUBSUB_OWNER) => SubscriptionElem #[xml(child(n = ..))]
] pub subscriptions: Vec<SubscriptionElem>,
); }
/// A subscription element, describing the state of a subscription. /// A subscription element, describing the state of a subscription.
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]

View file

@ -21,18 +21,18 @@ use minidom::Element;
// TODO: a better solution would be to split this into a query and a result elements, like for // TODO: a better solution would be to split this into a query and a result elements, like for
// XEP-0030. // XEP-0030.
generate_element!( /// A list of affiliations you have on a service, or on a node.
/// A list of affiliations you have on a service, or on a node. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Affiliations, "affiliations", PUBSUB, #[xml(namespace = ns::PUBSUB, name = "affiliations")]
attributes: [ pub struct Affiliations {
/// The optional node name this request pertains to. /// The optional node name this request pertains to.
node: Option<NodeName> = "node", #[xml(attribute(default))]
], pub node: Option<NodeName>,
children: [
/// The actual list of affiliation elements. /// The actual list of affiliation elements.
affiliations: Vec<Affiliation> = ("affiliation", PUBSUB) => Affiliation #[xml(child(n = ..))]
] pub affiliations: Vec<Affiliation>,
); }
/// An affiliation element. /// An affiliation element.
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@ -77,25 +77,27 @@ pub struct Default {
// type_: Option<String>, // type_: Option<String>,
} }
generate_element!( /// A request for a list of items.
/// A request for a list of items. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Items, "items", PUBSUB, #[xml(namespace = ns::PUBSUB, name = "items")]
attributes: [ pub struct Items {
// TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1. // TODO: should be an xs:positiveInteger, that is, an unbounded int ≥ 1.
/// Maximum number of items returned. /// Maximum number of items returned.
max_items: Option<u32> = "max_items", #[xml(attribute(name = "max_items" /*sic!*/, default))]
pub max_items: Option<u32>,
/// The node queried by this request. /// The node queried by this request.
node: Required<NodeName> = "node", #[xml(attribute)]
pub node: NodeName,
/// The subscription identifier related to this request. /// The subscription identifier related to this request.
subid: Option<SubscriptionId> = "subid", #[xml(attribute(default))]
], pub subid: Option<SubscriptionId>,
children: [
/// The actual list of items returned. /// The actual list of items returned.
items: Vec<Item> = ("item", PUBSUB) => Item #[xml(child(n = ..))]
] pub items: Vec<Item>,
); }
impl Items { impl Items {
/// Create a new items request. /// Create a new items request.
@ -136,18 +138,18 @@ pub struct Options {
pub form: Option<DataForm>, pub form: Option<DataForm>,
} }
generate_element!( /// Request to publish items to a node.
/// Request to publish items to a node. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Publish, "publish", PUBSUB, #[xml(namespace = ns::PUBSUB, name = "publish")]
attributes: [ pub struct Publish {
/// The target node for this operation. /// The target node for this operation.
node: Required<NodeName> = "node", #[xml(attribute)]
], pub node: NodeName,
children: [
/// The items you want to publish. /// The items you want to publish.
items: Vec<Item> = ("item", PUBSUB) => Item #[xml(child(n = ..))]
] pub items: Vec<Item>,
); }
/// The options associated to a publish request. /// The options associated to a publish request.
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)] #[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
@ -259,18 +261,18 @@ pub struct Subscribe {
pub node: Option<NodeName>, pub node: Option<NodeName>,
} }
generate_element!( /// A request for current subscriptions.
/// A request for current subscriptions. #[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
Subscriptions, "subscriptions", PUBSUB, #[xml(namespace = ns::PUBSUB, name = "subscriptions")]
attributes: [ pub struct Subscriptions {
/// The node to query. /// The node to query.
node: Option<NodeName> = "node", #[xml(attribute(default))]
], pub node: Option<NodeName>,
children: [
/// The list of subscription elements returned. /// The list of subscription elements returned.
subscription: Vec<SubscriptionElem> = ("subscription", PUBSUB) => SubscriptionElem #[xml(child(n = ..))]
] pub subscription: Vec<SubscriptionElem>,
); }
/// A subscription element, describing the state of a subscription. /// A subscription element, describing the state of a subscription.
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)] #[derive(FromXml, AsXml, Debug, PartialEq, Clone)]

View file

@ -9,18 +9,18 @@ use xso::{AsXml, FromXml};
use crate::message::MessagePayload; use crate::message::MessagePayload;
use crate::ns; use crate::ns;
generate_element!( /// Container for a set of reactions.
/// Container for a set of reactions. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Reactions, "reactions", REACTIONS, #[xml(namespace = ns::REACTIONS, name = "reactions")]
attributes: [ pub struct Reactions {
/// The id of the message these reactions apply to. /// The id of the message these reactions apply to.
id: Required<String> = "id", #[xml(attribute)]
], pub id: String,
children: [
/// The list of reactions. /// The list of reactions.
reactions: Vec<Reaction> = ("reaction", REACTIONS) => Reaction, #[xml(child(n = ..))]
] pub reactions: Vec<Reaction>,
); }
impl MessagePayload for Reactions {} impl MessagePayload for Reactions {}

View file

@ -4,9 +4,13 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use xso::{AsXml, FromXml};
use jid::BareJid; use jid::BareJid;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
generate_elem_id!( generate_elem_id!(
/// Represents a group a contact is part of. /// Represents a group a contact is part of.
Group, Group,
@ -67,22 +71,22 @@ generate_element!(
] ]
); );
generate_element!( /// The contact list of the user.
/// The contact list of the user. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
Roster, "query", ROSTER, #[xml(namespace = ns::ROSTER, name = "query")]
attributes: [ pub struct Roster {
/// Version of the contact list. /// Version of the contact list.
/// ///
/// This is an opaque string that should only be sent back to the server on /// This is an opaque string that should only be sent back to the server on
/// a new connection, if this client is storing the contact list between /// a new connection, if this client is storing the contact list between
/// connections. /// connections.
ver: Option<String> = "ver" #[xml(attribute(default))]
], pub ver: Option<String>,
children: [
/// List of the contacts of the user. /// List of the contacts of the user.
items: Vec<Item> = ("item", ROSTER) => Item #[xml(child(n = ..))]
] pub items: Vec<Item>,
); }
impl IqGetPayload for Roster {} impl IqGetPayload for Roster {}
impl IqSetPayload for Roster {} impl IqSetPayload for Roster {}
@ -273,7 +277,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in query element."); assert_eq!(message, "Unknown child in Roster element.");
let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>" let elem: Element = "<query xmlns='jabber:iq:roster' coucou=''/>"
.parse() .parse()
@ -283,7 +287,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in query element."); assert_eq!(message, "Unknown attribute in Roster element.");
} }
#[test] #[test]

View file

@ -172,6 +172,20 @@ impl TryFrom<Element> for SetResult {
} }
} }
impl FromXml for SetResult {
type Builder = minidom_compat::FromEventsViaElement<SetResult>;
fn from_events(
qname: rxml::QName,
attrs: rxml::AttrMap,
) -> Result<Self::Builder, FromEventsError> {
if qname.0 != crate::ns::RSM || qname.1 != "set" {
return Err(FromEventsError::Mismatch { name: qname, attrs });
}
Self::Builder::new(qname, attrs)
}
}
impl From<SetResult> for Element { impl From<SetResult> for Element {
fn from(set: SetResult) -> Element { fn from(set: SetResult) -> Element {
let first = set.first.clone().map(|first| { let first = set.first.clone().map(|first| {
@ -193,6 +207,14 @@ impl From<SetResult> for Element {
} }
} }
impl AsXml for SetResult {
type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
minidom_compat::AsItemsViaElement::new(self.clone())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -762,6 +762,20 @@ macro_rules! impl_pubsub_item {
} }
} }
impl ::xso::FromXml for $item {
type Builder = ::xso::minidom_compat::FromEventsViaElement<$item>;
fn from_events(
qname: ::xso::exports::rxml::QName,
attrs: ::xso::exports::rxml::AttrMap,
) -> Result<Self::Builder, ::xso::error::FromEventsError> {
if qname.0 != crate::ns::$ns || qname.1 != "item" {
return Err(::xso::error::FromEventsError::Mismatch { name: qname, attrs });
}
Self::Builder::new(qname, attrs)
}
}
impl From<$item> for minidom::Element { impl From<$item> for minidom::Element {
fn from(item: $item) -> minidom::Element { fn from(item: $item) -> minidom::Element {
minidom::Element::builder("item", ns::$ns) minidom::Element::builder("item", ns::$ns)
@ -772,6 +786,14 @@ macro_rules! impl_pubsub_item {
} }
} }
impl ::xso::AsXml for $item {
type ItemIter<'x> = ::xso::minidom_compat::AsItemsViaElement<'x>;
fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, ::xso::error::Error> {
::xso::minidom_compat::AsItemsViaElement::new(self.clone())
}
}
impl ::std::ops::Deref for $item { impl ::std::ops::Deref for $item {
type Target = crate::pubsub::Item; type Target = crate::pubsub::Item;