diff --git a/parsers/src/data_forms.rs b/parsers/src/data_forms.rs index 988a94e194d35eedc57bb699eb4d61ed5953277b..cc444ac1457176ad0b389dac54997172ff352db5 100644 --- a/parsers/src/data_forms.rs +++ b/parsers/src/data_forms.rs @@ -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 for DataForm { } } +impl FromXml for DataForm { + type Builder = minidom_compat::FromEventsViaElement; + + fn from_events( + qname: rxml::QName, + attrs: rxml::AttrMap, + ) -> Result { + if qname.0 != crate::ns::DATA_FORMS || qname.1 != "x" { + return Err(FromEventsError::Mismatch { name: qname, attrs }); + } + Self::Builder::new(qname, attrs) + } +} + impl From for Element { fn from(form: DataForm) -> Element { Element::builder("x", ns::DATA_FORMS) @@ -381,6 +399,14 @@ impl From for Element { } } +impl AsXml for DataForm { + type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>; + + fn as_xml_iter(&self) -> Result, Error> { + minidom_compat::AsItemsViaElement::new(self.clone()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/parsers/src/disco.rs b/parsers/src/disco.rs index f2eb5f619bfbaa5175c9506fba27f47b0e36a81f..8540e12af9ab0226e649d4e98d6f413125b7161b 100644 --- a/parsers/src/disco.rs +++ b/parsers/src/disco.rs @@ -185,20 +185,21 @@ impl From for Element { } } -generate_element!( /// Structure representing a `` element. /// /// It should only be used in an ``, 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 = "node", -], -children: [ + #[xml(attribute(default))] + pub node: Option, + /// Optional paging via Result Set Management - rsm: Option = ("set", RSM) => SetQuery, -]); + #[xml(child(default))] + pub rsm: Option, +} impl IqGetPayload for DiscoItemsQuery {} diff --git a/parsers/src/legacy_omemo.rs b/parsers/src/legacy_omemo.rs index 8e8d2f2d85999e725aa33ea462e6f48a6f644422..278da5169c5a74f54d245f808fa2e5a57e46f3b8 100644 --- a/parsers/src/legacy_omemo.rs +++ b/parsers/src/legacy_omemo.rs @@ -88,20 +88,28 @@ pub struct PreKeyPublic { pub data: Vec, } -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", LEGACY_OMEMO) => SignedPreKeyPublic, - /// SignedPreKey signature - signed_pre_key_signature: Option = ("signedPreKeySignature", LEGACY_OMEMO) => SignedPreKeySignature, - /// IdentityKey public key - identity_key: Option = ("identityKey", LEGACY_OMEMO) => IdentityKey, - /// List of (single use) PreKeys - prekeys: Option = ("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, + + /// SignedPreKey signature + #[xml(child(default))] + pub signed_pre_key_signature: Option, + + /// IdentityKey public key + #[xml(child(default))] + pub identity_key: Option, + + /// List of (single use) PreKeys + #[xml(child(default))] + pub prekeys: Option, +} impl PubSubPayload for Bundle {} @@ -167,17 +175,19 @@ pub struct Payload { pub data: Vec, } -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", LEGACY_OMEMO) => Header, - /// Payload for MessageElement - payload: Option = ("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, +} impl MessagePayload for Encrypted {} diff --git a/parsers/src/muc/user.rs b/parsers/src/muc/user.rs index f89f8f80d731eb9dfb32a101aba00b7bfc3492ad..9e439f8114df398d1fcf548a4c9c2d33bdae882e 100644 --- a/parsers/src/muc/user.rs +++ b/parsers/src/muc/user.rs @@ -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 for Actor { } } +impl FromXml for Actor { + type Builder = minidom_compat::FromEventsViaElement; + + fn from_events( + qname: rxml::QName, + attrs: rxml::AttrMap, + ) -> Result { + if qname.0 != crate::ns::MUC_USER || qname.1 != "actor" { + return Err(FromEventsError::Mismatch { name: qname, attrs }); + } + Self::Builder::new(qname, attrs) + } +} + impl From for Element { fn from(actor: Actor) -> Element { let elem = Element::builder("actor", ns::MUC_USER); @@ -129,6 +144,14 @@ impl From for Element { } } +impl AsXml for Actor { + type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>; + + fn as_xml_iter(&self) -> Result, 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", +/// 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 = "jid", + /// The real JID of this user, if you are allowed to see it. + #[xml(attribute(default))] + jid: Option, - /// The current nickname of this user. - nick: Option = "nick", + /// The current nickname of this user. + #[xml(attribute(default))] + nick: Option, - /// The current role of this user. - role: Required = "role", - ], children: [ - /// The actor affected by this item. - actor: Option = ("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", MUC_USER) => Continue, + /// The actor affected by this item. + #[xml(child(default))] + actor: Option, - /// A reason for this item. - reason: Option = ("reason", MUC_USER) => Reason - ] -); + /// Whether this continues a one-to-one discussion. + #[xml(child(default))] + continue_: Option, + + /// A reason for this item. + #[xml(child(default))] + reason: Option, +} 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 = "" .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() ); } diff --git a/parsers/src/pubsub/owner.rs b/parsers/src/pubsub/owner.rs index e33027fdd4125a83ca792b4485b684cf4033a399..94d49c4e49a397a57dfa0ca4b4bb4f8b8de244f5 100644 --- a/parsers/src/pubsub/owner.rs +++ b/parsers/src/pubsub/owner.rs @@ -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 = "node", - ], - children: [ - /// The form to configure it. - form: Option = ("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, -generate_element!( - /// Request to change default configuration. - Default, "default", PUBSUB_OWNER, - children: [ - /// The form to configure it. - form: Option = ("x", DATA_FORMS) => DataForm - ] -); + /// The form to configure it. + #[xml(child(default))] + pub form: Option, +} -generate_element!( - /// Request to delete a node. - Delete, "delete", PUBSUB_OWNER, - attributes: [ - /// The node to be configured. - node: Required = "node", - ], - children: [ - /// Redirection to replace the deleted node. - redirect: Option = ("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, +} + +/// 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, +} /// A redirect element. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] diff --git a/parsers/src/pubsub/pubsub.rs b/parsers/src/pubsub/pubsub.rs index 1f2766b7252d5e1135a2bdf41c868ee8d6147564..4a4167d9efaf9f85d3fe66e3f13e7d885b26120b 100644 --- a/parsers/src/pubsub/pubsub.rs +++ b/parsers/src/pubsub/pubsub.rs @@ -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 = ("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, +} /// 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", +/// 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 = "node", + /// The node affected by this request. + #[xml(attribute(default))] + pub node: Option, - /// The subscription identifier affected by this request. - subid: Option = "subid", - ], - children: [ - /// The form describing the subscription. - form: Option = ("x", DATA_FORMS) => DataForm - ] -); + /// The subscription identifier affected by this request. + #[xml(attribute(default))] + pub subid: Option, + + /// The form describing the subscription. + #[xml(child(default))] + pub form: Option, +} 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 = ("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, +} generate_attribute!( /// Whether a retract request should notify subscribers or not. @@ -209,6 +212,20 @@ impl TryFrom for SubscribeOptions { } } +impl FromXml for SubscribeOptions { + type Builder = minidom_compat::FromEventsViaElement; + + fn from_events( + qname: rxml::QName, + attrs: rxml::AttrMap, + ) -> Result { + if qname.0 != crate::ns::PUBSUB || qname.1 != "subscribe-options" { + return Err(FromEventsError::Mismatch { name: qname, attrs }); + } + Self::Builder::new(qname, attrs) + } +} + impl From for Element { fn from(subscribe_options: SubscribeOptions) -> Element { Element::builder("subscribe-options", ns::PUBSUB) @@ -221,6 +238,14 @@ impl From for Element { } } +impl AsXml for SubscribeOptions { + type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>; + + fn as_xml_iter(&self) -> Result, 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", +/// 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 = "node", + /// The node affected by this subscription. + #[xml(attribute(default))] + node: Option, - /// The subscription identifier for this subscription. - subid: Option = "subid", + /// The subscription identifier for this subscription. + #[xml(attribute(default))] + subid: Option, - /// The state of the subscription. - subscription: Option = "subscription", - ], - children: [ - /// The options related to this subscription. - subscribe_options: Option = ("subscribe-options", PUBSUB) => SubscribeOptions - ] -); + /// The state of the subscription. + #[xml(attribute(default))] + subscription: Option, + + /// The options related to this subscription. + #[xml(child(default))] + subscribe_options: Option, +} /// An unsubscribe request. #[derive(FromXml, AsXml, Debug, PartialEq, Clone)] diff --git a/parsers/src/rsm.rs b/parsers/src/rsm.rs index 69c885afc9736c82cc33d53fcf74d9313ef6c73d..51b06769d7ae36e188c7e7d56f0e1e3d483a78b6 100644 --- a/parsers/src/rsm.rs +++ b/parsers/src/rsm.rs @@ -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 for SetQuery { } } +impl FromXml for SetQuery { + type Builder = minidom_compat::FromEventsViaElement; + + fn from_events( + qname: rxml::QName, + attrs: rxml::AttrMap, + ) -> Result { + if qname.0 != crate::ns::RSM || qname.1 != "set" { + return Err(FromEventsError::Mismatch { name: qname, attrs }); + } + Self::Builder::new(qname, attrs) + } +} + impl From for Element { fn from(set: SetQuery) -> Element { Element::builder("set", ns::RSM) @@ -93,6 +111,14 @@ impl From for Element { } } +impl AsXml for SetQuery { + type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>; + + fn as_xml_iter(&self) -> Result, Error> { + minidom_compat::AsItemsViaElement::new(self.clone()) + } +} + /// Describes the paging result of a [query](struct.SetQuery.html). #[derive(Debug, Clone, PartialEq)] pub struct SetResult { diff --git a/parsers/src/sm.rs b/parsers/src/sm.rs index c22ee03f04b0dc23e6be92b3a370a34914b976fb..10bbc0a3a5265b4bbcdd8883d549f2734e4d218a 100644 --- a/parsers/src/sm.rs +++ b/parsers/src/sm.rs @@ -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 = "h", - ], - children: [ - /// The error returned. - // XXX: implement the * handling. - error: Option = ("*", 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, + + /// The error returned. + // XXX: implement the * handling. + #[xml(child(default))] + pub error: Option, +} /// Requests the currently received stanzas by the other party. #[derive(FromXml, AsXml, PartialEq, Debug, Clone)] diff --git a/parsers/src/util/macros.rs b/parsers/src/util/macros.rs index d7ffd563c10b453b57df1798cb9015cb44decaa0..a87e366833649abe88106105cbcdd61abdff0ee2 100644 --- a/parsers/src/util/macros.rs +++ b/parsers/src/util/macros.rs @@ -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 { + 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, ::xso::error::Error> { + ::xso::minidom_compat::AsItemsViaElement::new(self.clone()) + } + } ); } diff --git a/parsers/src/vcard_update.rs b/parsers/src/vcard_update.rs index ea16b9a454919fae6c4c86d7fd1cf6764e08b676..702d41f27c7c2142c3a2aaf9ce708331b44c55bd 100644 --- a/parsers/src/vcard_update.rs +++ b/parsers/src/vcard_update.rs @@ -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", 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, +} /// The photo element containing the avatar metadata #[derive(FromXml, AsXml, PartialEq, Debug, Clone)]