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
parent 4e9c4883a3
commit cea246a0fc
13 changed files with 198 additions and 149 deletions

View file

@ -24,7 +24,7 @@ chrono = { version = "0.4.5", default-features = false, features = ["std"] }
# same repository dependencies
jid = { version = "0.10", features = ["minidom"], path = "../jid" }
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]
# 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{FromXml, IntoXml};
use crate::date::DateTime;
use crate::ns;
use crate::presence::PresencePayload;
generate_element!(
/// Represents the last time the user interacted with their system.
Idle, "idle", IDLE,
attributes: [
/// The time at which the user stopped interacting.
since: Required<DateTime> = "since",
]
);
/// Represents the last time the user interacted with their system.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::IDLE, name = "idle")]
pub struct Idle {
/// The time at which the user stopped interacting.
#[xml(attribute)]
pub since: DateTime,
}
impl PresencePayload for Idle {}
@ -40,15 +43,16 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<idle xmlns='urn:xmpp:idle:1'><coucou/></idle>"
.parse()
.unwrap();
let elem: Element =
"<idle xmlns='urn:xmpp:idle:1' since='2017-05-21T20:19:55+01:00'><coucou/></idle>"
.parse()
.unwrap();
let error = Idle::try_from(elem).unwrap_err();
let message = match error {
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]
@ -59,7 +63,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(),
};
assert_eq!(message, "Required attribute 'since' missing.");
assert_eq!(
message,
"Required attribute field 'since' on Idle element missing."
);
}
#[test]

View file

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

View file

@ -4,7 +4,10 @@
// 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::{FromXml, IntoXml};
use crate::jingle::ContentId;
use crate::ns;
generate_attribute!(
/// The semantics of the grouping.
@ -17,14 +20,14 @@ generate_attribute!(
}
);
generate_element!(
/// Describes a content that should be grouped with other ones.
Content, "content", JINGLE_GROUPING,
attributes: [
/// The name of the matching [`Content`](crate::jingle::Content).
name: Required<ContentId> = "name",
]
);
/// Describes a content that should be grouped with other ones.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::JINGLE_GROUPING, name = "content")]
pub struct Content {
/// The name of the matching [`Content`](crate::jingle::Content).
#[xml(attribute)]
pub name: ContentId,
}
impl Content {
/// 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
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
generate_element!(
/// A Jingle thumbnail.
Thumbnail, "thumbnail", JINGLE_THUMBNAILS,
attributes: [
/// The URI of the thumbnail.
uri: Required<String> = "uri",
/// The media type of the thumbnail.
media_type: Required<String> = "media-type",
/// The width of the thumbnail.
width: Required<u32> = "width",
/// The height of the thumbnail.
height: Required<u32> = "height",
]
);
use xso::{FromXml, IntoXml};
use crate::ns;
/// A Jingle thumbnail.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::JINGLE_THUMBNAILS, name = "thumbnail")]
pub struct Thumbnail {
/// The URI of the thumbnail.
#[xml(attribute)]
pub uri: String,
/// 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)]
mod tests {

View file

@ -4,18 +4,21 @@
// 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::{FromXml, IntoXml};
use crate::message::MessagePayload;
use crate::ns;
use crate::pubsub::PubSubPayload;
use crate::util::text_node_codecs::{Base64, Codec};
generate_element!(
/// Element of the device list
Device, "device", LEGACY_OMEMO,
attributes: [
/// Device id
id: Required<u32> = "id"
]
);
/// Element of the device list
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::LEGACY_OMEMO, name = "device")]
pub struct Device {
/// Device id
#[xml(attribute)]
pub id: u32,
}
generate_element!(
/// 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.
Subscribe, "subscribe", MIX_CORE,
attributes: [
/// The PubSub node to subscribe to.
node: Required<NodeName> = "node",
]
);
/// A node to subscribe to.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::MIX_CORE, name = "subscribe")]
pub struct Subscribe {
/// The PubSub node to subscribe to.
#[xml(attribute)]
pub node: NodeName,
}
impl Subscribe {
/// Create a new Subscribe element.
@ -230,14 +230,14 @@ impl Create {
}
}
generate_element!(
/// Destroy a given MIX channel.
Destroy, "destroy", MIX_CORE,
attributes: [
/// The channel identifier to be destroyed.
channel: Required<ChannelId> = "channel",
]
);
/// Destroy a given MIX channel.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::MIX_CORE, name = "destroy")]
pub struct Destroy {
/// The channel identifier to be destroyed.
#[xml(attribute)]
pub channel: ChannelId,
}
// TODO: section 7.3.4, example 33, doesnt mirror the <destroy/> in the iq result unlike every
// 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{FromXml, IntoXml};
use crate::date::DateTime;
use crate::ns;
use crate::pubsub::PubSubPayload;
use crate::util::text_node_codecs::{Base64, Codec};
@ -33,16 +36,18 @@ generate_element!(
impl PubSubPayload for PubKey {}
generate_element!(
/// Public key metadata
PubKeyMeta, "pubkey-metadata", OX,
attributes: [
/// OpenPGP v4 fingerprint
v4fingerprint: Required<String> = "v4-fingerprint",
/// Time the key was published or updated
date: Required<DateTime> = "date",
]
);
/// Public key metadata
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::OX, name = "pubkey-metadata")]
pub struct PubKeyMeta {
/// OpenPGP v4 fingerprint
#[xml(attribute = "v4-fingerprint")]
pub v4fingerprint: String,
/// Time the key was published or updated
#[xml(attribute = "date")]
pub date: DateTime,
}
generate_element!(
/// List of public key metadata

View file

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

View file

@ -12,7 +12,10 @@ use crate::pubsub::{
};
use crate::Element;
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
// XEP-0030.
@ -29,17 +32,18 @@ generate_element!(
]
);
generate_element!(
/// An affiliation element.
Affiliation, "affiliation", PUBSUB,
attributes: [
/// The node this affiliation pertains to.
node: Required<NodeName> = "node",
/// An affiliation element.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB, name = "affiliation")]
pub struct Affiliation {
/// The node this affiliation pertains to.
#[xml(attribute)]
pub node: NodeName,
/// The affiliation you currently have on this node.
affiliation: Required<AffiliationAttribute> = "affiliation",
]
);
/// The affiliation you currently have on this node.
#[xml(attribute)]
pub affiliation: AffiliationAttribute,
}
generate_element!(
/// 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{FromXml, IntoXml};
use crate::ns;
use crate::util::text_node_codecs::{Codec, OptionalCodec, Text};
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
/// pauses between key presses.
Wait, "w", RTT,
attributes: [
/// Amount of milliseconds to wait before the next action.
time: Required<u32> = "n",
]
);
/// Allow for the transmission of intervals, between real-time text actions, to recreate the
/// pauses between key presses.
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::RTT, name = "w")]
pub struct Wait {
/// Amount of milliseconds to wait before the next action.
#[xml(attribute = "n")]
pub time: u32,
}
impl TryFrom<Action> for Wait {
type Error = Error;

View file

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

View file

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