Detailed changes
@@ -4,7 +4,11 @@
// 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/.
-use xso::error::{Error, FromElementError};
+use xso::{
+ error::{Error, FromElementError, FromEventsError},
+ exports::rxml,
+ minidom_compat, AsXml, FromXml,
+};
use crate::data_forms_validate::Validate;
use crate::media_element::MediaElement;
@@ -358,6 +362,20 @@ impl TryFrom<Element> for DataForm {
}
}
+impl FromXml for DataForm {
+ type Builder = minidom_compat::FromEventsViaElement<DataForm>;
+
+ fn from_events(
+ qname: rxml::QName,
+ attrs: rxml::AttrMap,
+ ) -> Result<Self::Builder, FromEventsError> {
+ if qname.0 != crate::ns::DATA_FORMS || qname.1 != "x" {
+ return Err(FromEventsError::Mismatch { name: qname, attrs });
+ }
+ Self::Builder::new(qname, attrs)
+ }
+}
+
impl From<DataForm> for Element {
fn from(form: DataForm) -> Element {
Element::builder("x", ns::DATA_FORMS)
@@ -381,6 +399,14 @@ impl From<DataForm> for Element {
}
}
+impl AsXml for DataForm {
+ type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
+
+ fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
+ minidom_compat::AsItemsViaElement::new(self.clone())
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -185,20 +185,21 @@ impl From<DiscoInfoResult> for Element {
}
}
-generate_element!(
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#items'/>` element.
///
/// It should only be used in an `<iq type='get'/>`, as it can only represent
/// the request, and not a result.
-DiscoItemsQuery, "query", DISCO_ITEMS,
-attributes: [
+#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
+#[xml(namespace = ns::DISCO_ITEMS, name = "query")]
+pub struct DiscoItemsQuery {
/// Node on which we are doing the discovery.
- node: Option<String> = "node",
-],
-children: [
+ #[xml(attribute(default))]
+ pub node: Option<String>,
+
/// Optional paging via Result Set Management
- rsm: Option<crate::rsm::SetQuery> = ("set", RSM) => SetQuery,
-]);
+ #[xml(child(default))]
+ pub rsm: Option<SetQuery>,
+}
impl IqGetPayload for DiscoItemsQuery {}
@@ -88,20 +88,28 @@ pub struct PreKeyPublic {
pub data: Vec<u8>,
}
-generate_element!(
- /// A collection of publicly accessible data that can be used to build a session with a device, namely its public IdentityKey, a signed PreKey with corresponding signature, and a list of (single use) PreKeys.
- Bundle, "bundle", LEGACY_OMEMO,
- children: [
- /// SignedPreKey public key
- signed_pre_key_public: Option<SignedPreKeyPublic> = ("signedPreKeyPublic", LEGACY_OMEMO) => SignedPreKeyPublic,
- /// SignedPreKey signature
- signed_pre_key_signature: Option<SignedPreKeySignature> = ("signedPreKeySignature", LEGACY_OMEMO) => SignedPreKeySignature,
- /// IdentityKey public key
- identity_key: Option<IdentityKey> = ("identityKey", LEGACY_OMEMO) => IdentityKey,
- /// List of (single use) PreKeys
- prekeys: Option<Prekeys> = ("prekeys", LEGACY_OMEMO) => Prekeys,
- ]
-);
+/// A collection of publicly accessible data that can be used to build a
+/// session with a device, namely its public IdentityKey, a signed PreKey with
+/// corresponding signature, and a list of (single use) PreKeys.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "bundle")]
+pub struct Bundle {
+ /// SignedPreKey public key
+ #[xml(child(default))]
+ pub signed_pre_key_public: Option<SignedPreKeyPublic>,
+
+ /// SignedPreKey signature
+ #[xml(child(default))]
+ pub signed_pre_key_signature: Option<SignedPreKeySignature>,
+
+ /// IdentityKey public key
+ #[xml(child(default))]
+ pub identity_key: Option<IdentityKey>,
+
+ /// List of (single use) PreKeys
+ #[xml(child(default))]
+ pub prekeys: Option<Prekeys>,
+}
impl PubSubPayload for Bundle {}
@@ -167,17 +175,19 @@ pub struct Payload {
pub data: Vec<u8>,
}
-generate_element!(
- /// An OMEMO element, which can be either a MessageElement (with payload),
- /// or a KeyTransportElement (without payload).
- Encrypted, "encrypted", LEGACY_OMEMO,
- children: [
- /// The header contains encrypted keys for a message
- header: Required<Header> = ("header", LEGACY_OMEMO) => Header,
- /// Payload for MessageElement
- payload: Option<Payload> = ("payload", LEGACY_OMEMO) => Payload
- ]
-);
+/// An OMEMO element, which can be either a MessageElement (with payload),
+/// or a KeyTransportElement (without payload).
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::LEGACY_OMEMO, name = "encrypted")]
+pub struct Encrypted {
+ /// The header contains encrypted keys for a message
+ #[xml(child)]
+ pub header: Header,
+
+ /// Payload for MessageElement
+ #[xml(child(default))]
+ pub payload: Option<Payload>,
+}
impl MessagePayload for Encrypted {}
@@ -6,8 +6,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{
- error::{Error, FromElementError},
- AsXml, FromXml,
+ error::{Error, FromElementError, FromEventsError},
+ exports::rxml,
+ minidom_compat, AsXml, FromXml,
};
use crate::message::MessagePayload;
@@ -117,6 +118,20 @@ impl TryFrom<Element> for Actor {
}
}
+impl FromXml for Actor {
+ type Builder = minidom_compat::FromEventsViaElement<Actor>;
+
+ fn from_events(
+ qname: rxml::QName,
+ attrs: rxml::AttrMap,
+ ) -> Result<Self::Builder, FromEventsError> {
+ if qname.0 != crate::ns::MUC_USER || qname.1 != "actor" {
+ return Err(FromEventsError::Mismatch { name: qname, attrs });
+ }
+ Self::Builder::new(qname, attrs)
+ }
+}
+
impl From<Actor> for Element {
fn from(actor: Actor) -> Element {
let elem = Element::builder("actor", ns::MUC_USER);
@@ -129,6 +144,14 @@ impl From<Actor> for Element {
}
}
+impl AsXml for Actor {
+ type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
+
+ fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
+ minidom_compat::AsItemsViaElement::new(self.clone())
+ }
+}
+
/// Used to continue a one-to-one discussion in a room, with more than one
/// participant.
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@@ -190,31 +213,38 @@ generate_attribute!(
}, Default = None
);
-generate_element!(
- /// An item representing a user in a room.
- Item, "item", MUC_USER, attributes: [
- /// The affiliation of this user with the room.
- affiliation: Required<Affiliation> = "affiliation",
+/// An item representing a user in a room.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::MUC_USER, name = "item")]
+pub struct Item {
+ /// The affiliation of this user with the room.
+ #[xml(attribute)]
+ affiliation: Affiliation,
- /// The real JID of this user, if you are allowed to see it.
- jid: Option<FullJid> = "jid",
+ /// The real JID of this user, if you are allowed to see it.
+ #[xml(attribute(default))]
+ jid: Option<FullJid>,
- /// The current nickname of this user.
- nick: Option<String> = "nick",
+ /// The current nickname of this user.
+ #[xml(attribute(default))]
+ nick: Option<String>,
- /// The current role of this user.
- role: Required<Role> = "role",
- ], children: [
- /// The actor affected by this item.
- actor: Option<Actor> = ("actor", MUC_USER) => Actor,
+ /// The current role of this user.
+ #[xml(attribute)]
+ role: Role,
- /// Whether this continues a one-to-one discussion.
- continue_: Option<Continue> = ("continue", MUC_USER) => Continue,
+ /// The actor affected by this item.
+ #[xml(child(default))]
+ actor: Option<Actor>,
- /// A reason for this item.
- reason: Option<Reason> = ("reason", MUC_USER) => Reason
- ]
-);
+ /// Whether this continues a one-to-one discussion.
+ #[xml(child(default))]
+ continue_: Option<Continue>,
+
+ /// A reason for this item.
+ #[xml(child(default))]
+ reason: Option<Reason>,
+}
impl Item {
/// Creates a new item with the given affiliation and role.
@@ -590,6 +620,8 @@ mod tests {
#[test]
fn test_item_invalid_attr() {
let elem: Element = "<item xmlns='http://jabber.org/protocol/muc#user'
+ affiliation='member'
+ role='moderator'
foo='bar'/>"
.parse()
.unwrap();
@@ -598,7 +630,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(),
};
- assert_eq!(message, "Unknown attribute in item element.".to_owned());
+ assert_eq!(message, "Unknown attribute in Item element.".to_owned());
}
#[test]
@@ -622,7 +654,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(),
};
- assert_eq!(message, "Required attribute 'role' missing.".to_owned());
+ assert_eq!(
+ message,
+ "Required attribute field 'role' on Item element missing.".to_owned()
+ );
}
#[test]
@@ -652,7 +687,7 @@ mod tests {
};
assert_eq!(
message,
- "Required attribute 'affiliation' missing.".to_owned()
+ "Required attribute field 'affiliation' on Item element missing.".to_owned()
);
}
@@ -41,40 +41,40 @@ pub struct Affiliation {
affiliation: AffiliationAttribute,
}
-generate_element!(
- /// Request to configure a node.
- Configure, "configure", PUBSUB_OWNER,
- attributes: [
- /// The node to be configured.
- node: Option<NodeName> = "node",
- ],
- children: [
- /// The form to configure it.
- form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
- ]
-);
+/// 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>,
-generate_element!(
- /// Request to change default configuration.
- Default, "default", PUBSUB_OWNER,
- children: [
- /// The form to configure it.
- form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
- ]
-);
+ /// The form to configure it.
+ #[xml(child(default))]
+ pub form: Option<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: Option<Redirect> = ("redirect", PUBSUB_OWNER) => Redirect
- ]
-);
+/// 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)]
@@ -5,8 +5,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{
- error::{Error, FromElementError},
- AsXml, FromXml,
+ error::{Error, FromElementError, FromEventsError},
+ exports::rxml,
+ minidom_compat, AsXml, FromXml,
};
use crate::data_forms::DataForm;
@@ -46,14 +47,14 @@ pub struct Affiliation {
pub affiliation: AffiliationAttribute,
}
-generate_element!(
- /// Request to configure a new node.
- Configure, "configure", PUBSUB,
- children: [
- /// The form to configure it.
- form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
- ]
-);
+/// Request to configure a new node.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::PUBSUB, name = "configure")]
+pub struct Configure {
+ /// The form to configure it.
+ #[xml(child(default))]
+ pub form: Option<DataForm>,
+}
/// Request to create a new node.
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@@ -114,24 +115,26 @@ pub struct Item(pub PubSubItem);
impl_pubsub_item!(Item, PUBSUB);
-generate_element!(
- /// The options associated to a subscription request.
- Options, "options", PUBSUB,
- attributes: [
- /// The JID affected by this request.
- jid: Required<Jid> = "jid",
+/// The options associated to a subscription request.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::PUBSUB, name = "options")]
+pub struct Options {
+ /// The JID affected by this request.
+ #[xml(attribute)]
+ pub jid: Jid,
- /// The node affected by this request.
- node: Option<NodeName> = "node",
+ /// The node affected by this request.
+ #[xml(attribute(default))]
+ pub node: Option<NodeName>,
- /// The subscription identifier affected by this request.
- subid: Option<SubscriptionId> = "subid",
- ],
- children: [
- /// The form describing the subscription.
- form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
- ]
-);
+ /// The subscription identifier affected by this request.
+ #[xml(attribute(default))]
+ pub subid: Option<SubscriptionId>,
+
+ /// The form describing the subscription.
+ #[xml(child(default))]
+ pub form: Option<DataForm>,
+}
generate_element!(
/// Request to publish items to a node.
@@ -146,14 +149,14 @@ generate_element!(
]
);
-generate_element!(
- /// The options associated to a publish request.
- PublishOptions, "publish-options", PUBSUB,
- children: [
- /// The form describing these options.
- form: Option<DataForm> = ("x", DATA_FORMS) => DataForm
- ]
-);
+/// The options associated to a publish request.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::PUBSUB, name = "publish-options")]
+pub struct PublishOptions {
+ /// The form describing these options.
+ #[xml(child(default))]
+ pub form: Option<DataForm>,
+}
generate_attribute!(
/// Whether a retract request should notify subscribers or not.
@@ -209,6 +212,20 @@ impl TryFrom<Element> for SubscribeOptions {
}
}
+impl FromXml for SubscribeOptions {
+ type Builder = minidom_compat::FromEventsViaElement<SubscribeOptions>;
+
+ fn from_events(
+ qname: rxml::QName,
+ attrs: rxml::AttrMap,
+ ) -> Result<Self::Builder, FromEventsError> {
+ if qname.0 != crate::ns::PUBSUB || qname.1 != "subscribe-options" {
+ return Err(FromEventsError::Mismatch { name: qname, attrs });
+ }
+ Self::Builder::new(qname, attrs)
+ }
+}
+
impl From<SubscribeOptions> for Element {
fn from(subscribe_options: SubscribeOptions) -> Element {
Element::builder("subscribe-options", ns::PUBSUB)
@@ -221,6 +238,14 @@ impl From<SubscribeOptions> for Element {
}
}
+impl AsXml for SubscribeOptions {
+ type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
+
+ fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
+ minidom_compat::AsItemsViaElement::new(self.clone())
+ }
+}
+
/// A request to subscribe a JID to a node.
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::PUBSUB, name = "subscribe")]
@@ -247,27 +272,30 @@ generate_element!(
]
);
-generate_element!(
- /// A subscription element, describing the state of a subscription.
- SubscriptionElem, "subscription", PUBSUB,
- attributes: [
- /// The JID affected by this subscription.
- jid: Required<Jid> = "jid",
+/// A subscription element, describing the state of a subscription.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::PUBSUB, name = "subscription")]
+pub struct SubscriptionElem {
+ /// The JID affected by this subscription.
+ #[xml(attribute)]
+ jid: Jid,
- /// The node affected by this subscription.
- node: Option<NodeName> = "node",
+ /// The node affected by this subscription.
+ #[xml(attribute(default))]
+ node: Option<NodeName>,
- /// The subscription identifier for this subscription.
- subid: Option<SubscriptionId> = "subid",
+ /// The subscription identifier for this subscription.
+ #[xml(attribute(default))]
+ subid: Option<SubscriptionId>,
- /// The state of the subscription.
- subscription: Option<Subscription> = "subscription",
- ],
- children: [
- /// The options related to this subscription.
- subscribe_options: Option<SubscribeOptions> = ("subscribe-options", PUBSUB) => SubscribeOptions
- ]
-);
+ /// The state of the subscription.
+ #[xml(attribute(default))]
+ subscription: Option<Subscription>,
+
+ /// The options related to this subscription.
+ #[xml(child(default))]
+ subscribe_options: Option<SubscribeOptions>,
+}
/// An unsubscribe request.
#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
@@ -6,7 +6,11 @@
use crate::ns;
use minidom::Element;
-use xso::error::{Error, FromElementError};
+use xso::{
+ error::{Error, FromElementError, FromEventsError},
+ exports::rxml,
+ minidom_compat, AsXml, FromXml,
+};
/// Requests paging through a potentially big set of items (represented by an
/// UID).
@@ -67,6 +71,20 @@ impl TryFrom<Element> for SetQuery {
}
}
+impl FromXml for SetQuery {
+ type Builder = minidom_compat::FromEventsViaElement<SetQuery>;
+
+ 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<SetQuery> for Element {
fn from(set: SetQuery) -> Element {
Element::builder("set", ns::RSM)
@@ -93,6 +111,14 @@ impl From<SetQuery> for Element {
}
}
+impl AsXml for SetQuery {
+ type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
+
+ fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
+ minidom_compat::AsItemsViaElement::new(self.clone())
+ }
+}
+
/// Describes the paging result of a [query](struct.SetQuery.html).
#[derive(Debug, Clone, PartialEq)]
pub struct SetResult {
@@ -93,19 +93,19 @@ pub struct Enabled {
pub resume: ResumeAttr,
}
-generate_element!(
- /// A stream management error happened.
- Failed, "failed", SM,
- attributes: [
- /// The last handled stanza.
- h: Option<u32> = "h",
- ],
- children: [
- /// The error returned.
- // XXX: implement the * handling.
- error: Option<DefinedCondition> = ("*", XMPP_STANZAS) => DefinedCondition
- ]
-);
+/// A stream management error happened.
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::SM, name = "failed")]
+pub struct Failed {
+ /// The last handled stanza.
+ #[xml(attribute)]
+ pub h: Option<u32>,
+
+ /// The error returned.
+ // XXX: implement the * handling.
+ #[xml(child(default))]
+ pub error: Option<DefinedCondition>,
+}
/// Requests the currently received stanzas by the other party.
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
@@ -300,6 +300,24 @@ macro_rules! generate_element_enum {
})
}
}
+
+ impl ::xso::FromXml for $elem {
+ type Builder = ::xso::minidom_compat::FromEventsViaElement<$elem>;
+
+ 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 != $name {
+ return Err(::xso::error::FromEventsError::Mismatch {
+ name: qname,
+ attrs,
+ })
+ }
+ Self::Builder::new(qname, attrs)
+ }
+ }
+
impl From<$elem> for minidom::Element {
fn from(elem: $elem) -> minidom::Element {
minidom::Element::builder(
@@ -311,6 +329,14 @@ macro_rules! generate_element_enum {
.build()
}
}
+
+ impl ::xso::AsXml for $elem {
+ 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())
+ }
+ }
);
}
@@ -16,14 +16,14 @@ use xso::{text::FixedHex, AsXml, FromXml};
use crate::ns;
-generate_element!(
- /// The presence payload for an avatar VCard update
- VCardUpdate, "x", VCARD_UPDATE,
- children: [
- /// The photo element. Is empty if "a client is not yet ready to advertise an image".
- photo: Option<Photo> = ("photo", VCARD_UPDATE) => Photo,
- ]
-);
+/// The presence payload for an avatar VCard update
+#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
+#[xml(namespace = ns::VCARD_UPDATE, name = "x")]
+pub struct VCardUpdate {
+ /// The photo element. Is empty if "a client is not yet ready to advertise an image".
+ #[xml(child(default))]
+ pub photo: Option<Photo>,
+}
/// The photo element containing the avatar metadata
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]