parsers: port more generate_element! usages to derive macros

This commit is contained in:
Jonas Schäfer 2024-06-26 13:13:02 +02:00
commit cea246a0fc
13 changed files with 200 additions and 151 deletions

View file

@ -24,7 +24,7 @@ chrono = { version = "0.4.5", default-features = false, features = ["std"] }
# same repository dependencies # same repository dependencies
jid = { version = "0.10", features = ["minidom"], path = "../jid" } jid = { version = "0.10", features = ["minidom"], path = "../jid" }
minidom = { version = "0.15", path = "../minidom" } minidom = { version = "0.15", path = "../minidom" }
xso = { version = "0.0.2", features = ["macros", "minidom", "panicking-into-impl"] } xso = { version = "0.0.2", features = ["macros", "minidom", "panicking-into-impl", "jid"] }
[features] [features]
# Build xmpp-parsers to make components instead of clients. # Build xmpp-parsers to make components instead of clients.

View file

@ -4,17 +4,20 @@
// 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::{FromXml, IntoXml};
use crate::date::DateTime; use crate::date::DateTime;
use crate::ns;
use crate::presence::PresencePayload; use crate::presence::PresencePayload;
generate_element!( /// Represents the last time the user interacted with their system.
/// Represents the last time the user interacted with their system. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Idle, "idle", IDLE, #[xml(namespace = ns::IDLE, name = "idle")]
attributes: [ pub struct Idle {
/// The time at which the user stopped interacting. /// The time at which the user stopped interacting.
since: Required<DateTime> = "since", #[xml(attribute)]
] pub since: DateTime,
); }
impl PresencePayload for Idle {} impl PresencePayload for Idle {}
@ -40,15 +43,16 @@ mod tests {
#[test] #[test]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'><coucou/></idle>" let elem: Element =
.parse() "<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'><coucou/></idle>"
.unwrap(); .parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err(); let error = Idle::try_from(elem).unwrap_err();
let message = match error { let message = match error {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), other => panic!("unexpected result: {:?}", other),
}; };
assert_eq!(message, "Unknown child in idle element."); assert_eq!(message, "Unknown child in Idle element.");
} }
#[test] #[test]
@ -59,7 +63,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'since' missing."); assert_eq!(
message,
"Required attribute field 'since' on Idle element missing."
);
} }
#[test] #[test]

View file

@ -11,7 +11,10 @@ use crate::ns;
use minidom::{Element, Node}; use minidom::{Element, Node};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use xso::error::{Error, FromElementError}; use xso::{
error::{Error, FromElementError},
FromXml, IntoXml,
};
generate_element!( generate_element!(
/// Represents a range in a file. /// Represents a range in a file.
@ -319,17 +322,18 @@ impl From<Checksum> for Element {
} }
} }
generate_element!( /// A notice that the file transfer has been completed.
/// A notice that the file transfer has been completed. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Received, "received", JINGLE_FT, #[xml(namespace = ns::JINGLE_FT, name = "received")]
attributes: [ pub struct Received {
/// The content identifier of this Jingle session. /// The content identifier of this Jingle session.
name: Required<ContentId> = "name", #[xml(attribute)]
pub name: ContentId,
/// The creator of this file transfer. /// The creator of this file transfer.
creator: Required<Creator> = "creator", #[xml(attribute)]
] pub creator: Creator,
); }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -479,7 +483,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in received element."); assert_eq!(message, "Unknown child in Received element.");
let elem: Element = let elem: Element =
"<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'/>" "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' creator='initiator'/>"
@ -490,7 +494,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'name' missing."); assert_eq!(
message,
"Required attribute field 'name' on Received element missing."
);
let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='coucou'/>".parse().unwrap(); let elem: Element = "<received xmlns='urn:xmpp:jingle:apps:file-transfer:5' name='coucou' creator='coucou'/>".parse().unwrap();
let error = Received::try_from(elem).unwrap_err(); let error = Received::try_from(elem).unwrap_err();
@ -513,7 +520,7 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown attribute in received element."); assert_eq!(message, "Unknown attribute in Received element.");
} }
#[test] #[test]

View file

@ -4,7 +4,10 @@
// 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::{FromXml, IntoXml};
use crate::jingle::ContentId; use crate::jingle::ContentId;
use crate::ns;
generate_attribute!( generate_attribute!(
/// The semantics of the grouping. /// The semantics of the grouping.
@ -17,14 +20,14 @@ generate_attribute!(
} }
); );
generate_element!( /// Describes a content that should be grouped with other ones.
/// Describes a content that should be grouped with other ones. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Content, "content", JINGLE_GROUPING, #[xml(namespace = ns::JINGLE_GROUPING, name = "content")]
attributes: [ pub struct Content {
/// The name of the matching [`Content`](crate::jingle::Content). /// The name of the matching [`Content`](crate::jingle::Content).
name: Required<ContentId> = "name", #[xml(attribute)]
] pub name: ContentId,
); }
impl Content { impl Content {
/// Creates a new \<content/\> element. /// Creates a new \<content/\> element.

View file

@ -6,20 +6,30 @@
// 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/.
generate_element!( use xso::{FromXml, IntoXml};
/// A Jingle thumbnail.
Thumbnail, "thumbnail", JINGLE_THUMBNAILS, use crate::ns;
attributes: [
/// The URI of the thumbnail. /// A Jingle thumbnail.
uri: Required<String> = "uri", #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
/// The media type of the thumbnail. #[xml(namespace = ns::JINGLE_THUMBNAILS, name = "thumbnail")]
media_type: Required<String> = "media-type", pub struct Thumbnail {
/// The width of the thumbnail. /// The URI of the thumbnail.
width: Required<u32> = "width", #[xml(attribute)]
/// The height of the thumbnail. pub uri: String,
height: Required<u32> = "height",
] /// The media type of the thumbnail.
); #[xml(attribute = "media-type")]
pub media_type: String,
/// The width of the thumbnail.
#[xml(attribute)]
pub width: u32,
/// The height of the thumbnail.
#[xml(attribute)]
pub height: u32,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -4,18 +4,21 @@
// 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::{FromXml, IntoXml};
use crate::message::MessagePayload; use crate::message::MessagePayload;
use crate::ns;
use crate::pubsub::PubSubPayload; use crate::pubsub::PubSubPayload;
use crate::util::text_node_codecs::{Base64, Codec}; use crate::util::text_node_codecs::{Base64, Codec};
generate_element!( /// Element of the device list
/// Element of the device list #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Device, "device", LEGACY_OMEMO, #[xml(namespace = ns::LEGACY_OMEMO, name = "device")]
attributes: [ pub struct Device {
/// Device id /// Device id
id: Required<u32> = "id" #[xml(attribute)]
] pub id: u32,
); }
generate_element!( 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

View file

@ -58,14 +58,14 @@ impl Participant {
} }
} }
generate_element!( /// A node to subscribe to.
/// A node to subscribe to. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Subscribe, "subscribe", MIX_CORE, #[xml(namespace = ns::MIX_CORE, name = "subscribe")]
attributes: [ pub struct Subscribe {
/// The PubSub node to subscribe to. /// The PubSub node to subscribe to.
node: Required<NodeName> = "node", #[xml(attribute)]
] pub node: NodeName,
); }
impl Subscribe { impl Subscribe {
/// Create a new Subscribe element. /// Create a new Subscribe element.
@ -230,14 +230,14 @@ impl Create {
} }
} }
generate_element!( /// Destroy a given MIX channel.
/// Destroy a given MIX channel. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Destroy, "destroy", MIX_CORE, #[xml(namespace = ns::MIX_CORE, name = "destroy")]
attributes: [ pub struct Destroy {
/// The channel identifier to be destroyed. /// The channel identifier to be destroyed.
channel: Required<ChannelId> = "channel", #[xml(attribute)]
] pub channel: ChannelId,
); }
// TODO: section 7.3.4, example 33, doesnt mirror the <destroy/> in the iq result unlike every // TODO: section 7.3.4, example 33, doesnt mirror the <destroy/> in the iq result unlike every
// other section so far. // other section so far.

View file

@ -4,7 +4,10 @@
// 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::{FromXml, IntoXml};
use crate::date::DateTime; use crate::date::DateTime;
use crate::ns;
use crate::pubsub::PubSubPayload; use crate::pubsub::PubSubPayload;
use crate::util::text_node_codecs::{Base64, Codec}; use crate::util::text_node_codecs::{Base64, Codec};
@ -33,16 +36,18 @@ generate_element!(
impl PubSubPayload for PubKey {} impl PubSubPayload for PubKey {}
generate_element!( /// Public key metadata
/// Public key metadata #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
PubKeyMeta, "pubkey-metadata", OX, #[xml(namespace = ns::OX, name = "pubkey-metadata")]
attributes: [ pub struct PubKeyMeta {
/// OpenPGP v4 fingerprint /// OpenPGP v4 fingerprint
v4fingerprint: Required<String> = "v4-fingerprint", #[xml(attribute = "v4-fingerprint")]
/// Time the key was published or updated pub v4fingerprint: String,
date: Required<DateTime> = "date",
] /// Time the key was published or updated
); #[xml(attribute = "date")]
pub date: DateTime,
}
generate_element!( generate_element!(
/// List of public key metadata /// List of public key metadata

View file

@ -28,17 +28,18 @@ generate_element!(
] ]
); );
generate_element!( /// An affiliation element.
/// An affiliation element. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Affiliation, "affiliation", PUBSUB_OWNER, #[xml(namespace = ns::PUBSUB_OWNER, name = "affiliation")]
attributes: [ pub struct Affiliation {
/// The node this affiliation pertains to. /// The node this affiliation pertains to.
jid: Required<Jid> = "jid", #[xml(attribute)]
jid: Jid,
/// The affiliation you currently have on this node. /// The affiliation you currently have on this node.
affiliation: Required<AffiliationAttribute> = "affiliation", #[xml(attribute)]
] affiliation: AffiliationAttribute,
); }
generate_element!( generate_element!(
/// Request to configure a node. /// Request to configure a node.
@ -84,14 +85,14 @@ pub struct Redirect {
pub uri: String, pub uri: String,
} }
generate_element!( /// Request to clear a node.
/// Request to delete a node. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Purge, "purge", PUBSUB_OWNER, #[xml(namespace = ns::PUBSUB_OWNER, name = "purge")]
attributes: [ pub struct Purge {
/// The node to be configured. /// The node to be cleared.
node: Required<NodeName> = "node", #[xml(attribute)]
] pub node: NodeName,
); }
generate_element!( generate_element!(
/// A request for current subscriptions. /// A request for current subscriptions.

View file

@ -12,7 +12,10 @@ use crate::pubsub::{
}; };
use crate::Element; use crate::Element;
use jid::Jid; use jid::Jid;
use xso::error::{Error, FromElementError}; use xso::{
error::{Error, FromElementError},
FromXml, IntoXml,
};
// 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.
@ -29,17 +32,18 @@ generate_element!(
] ]
); );
generate_element!( /// An affiliation element.
/// An affiliation element. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Affiliation, "affiliation", PUBSUB, #[xml(namespace = ns::PUBSUB, name = "affiliation")]
attributes: [ pub struct Affiliation {
/// The node this affiliation pertains to. /// The node this affiliation pertains to.
node: Required<NodeName> = "node", #[xml(attribute)]
pub node: NodeName,
/// The affiliation you currently have on this node. /// The affiliation you currently have on this node.
affiliation: Required<AffiliationAttribute> = "affiliation", #[xml(attribute)]
] pub affiliation: AffiliationAttribute,
); }
generate_element!( generate_element!(
/// Request to configure a new node. /// Request to configure a new node.

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::{FromXml, IntoXml};
use crate::ns; use crate::ns;
use crate::util::text_node_codecs::{Codec, OptionalCodec, Text}; use crate::util::text_node_codecs::{Codec, OptionalCodec, Text};
use crate::Element; use crate::Element;
@ -110,16 +112,15 @@ impl TryFrom<Action> for Erase {
} }
} }
generate_element!( /// Allow for the transmission of intervals, between real-time text actions, to recreate the
/// Allow for the transmission of intervals, between real-time text actions, to recreate the /// pauses between key presses.
/// pauses between key presses. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
Wait, "w", RTT, #[xml(namespace = ns::RTT, name = "w")]
pub struct Wait {
attributes: [ /// Amount of milliseconds to wait before the next action.
/// Amount of milliseconds to wait before the next action. #[xml(attribute = "n")]
time: Required<u32> = "n", pub time: u32,
] }
);
impl TryFrom<Action> for Wait { impl TryFrom<Action> for Wait {
type Error = Error; type Error = Error;

View file

@ -9,14 +9,14 @@ use xso::{FromXml, IntoXml};
use crate::ns; use crate::ns;
use crate::stanza_error::DefinedCondition; use crate::stanza_error::DefinedCondition;
generate_element!( /// Acknowledgement of the currently received stanzas.
/// Acknowledgement of the currently received stanzas. #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
A, "a", SM, #[xml(namespace = ns::SM, name = "a")]
attributes: [ pub struct A {
/// The last handled stanza. /// The last handled stanza.
h: Required<u32> = "h", #[xml(attribute)]
] pub h: u32,
); }
impl A { impl A {
/// Generates a new `<a/>` element. /// Generates a new `<a/>` element.

View file

@ -10,18 +10,19 @@ use crate::message::MessagePayload;
use crate::ns; use crate::ns;
use jid::Jid; use jid::Jid;
generate_element!( /// Gives the identifier a service has stamped on this stanza, often in
/// Gives the identifier a service has stamped on this stanza, often in /// order to identify it inside of [an archive](../mam/index.html).
/// order to identify it inside of [an archive](../mam/index.html). #[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
StanzaId, "stanza-id", SID, #[xml(namespace = ns::SID, name = "stanza-id")]
attributes: [ pub struct StanzaId {
/// The id associated to this stanza by another entity. /// The id associated to this stanza by another entity.
id: Required<String> = "id", #[xml(attribute)]
pub id: String,
/// The entity who stamped this stanza-id. /// The entity who stamped this stanza-id.
by: Required<Jid> = "by", #[xml(attribute)]
] pub by: Jid,
); }
impl MessagePayload for StanzaId {} impl MessagePayload for StanzaId {}
@ -76,15 +77,16 @@ mod tests {
#[test] #[test]
fn test_invalid_child() { fn test_invalid_child() {
let elem: Element = "<stanza-id xmlns='urn:xmpp:sid:0'><coucou/></stanza-id>" let elem: Element =
.parse() "<stanza-id xmlns='urn:xmpp:sid:0' by='a@b' id='x'><coucou/></stanza-id>"
.unwrap(); .parse()
.unwrap();
let error = StanzaId::try_from(elem).unwrap_err(); let error = StanzaId::try_from(elem).unwrap_err();
let message = match error { let message = match error {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Unknown child in stanza-id element."); assert_eq!(message, "Unknown child in StanzaId element.");
} }
#[test] #[test]
@ -95,7 +97,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'id' missing."); assert_eq!(
message,
"Required attribute field 'id' on StanzaId element missing."
);
} }
#[test] #[test]
@ -108,7 +113,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string, FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(message, "Required attribute 'by' missing."); assert_eq!(
message,
"Required attribute field 'by' on StanzaId element missing."
);
} }
#[test] #[test]