parsers: port more elements to derive macros

This commit is contained in:
Jonas Schäfer 2024-06-29 16:34:27 +02:00
parent 2fb9fc6959
commit 0e4865006c
18 changed files with 386 additions and 255 deletions

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::hashes::Sha1HexAttribute;
use crate::ns;
use crate::pubsub::PubSubPayload;
use crate::util::text_node_codecs::{Codec, WhitespaceAwareBase64};
@ -19,29 +22,34 @@ generate_element!(
impl PubSubPayload for Metadata {}
generate_element!(
/// Communicates avatar metadata.
Info, "info", AVATAR_METADATA,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::AVATAR_METADATA, name = "info")]
pub struct Info {
/// The size of the image data in bytes.
bytes: Required<u32> = "bytes",
#[xml(attribute)]
pub bytes: u32,
/// The width of the image in pixels.
width: Option<u16> = "width",
#[xml(attribute(default))]
pub width: Option<u16>,
/// The height of the image in pixels.
height: Option<u16> = "height",
#[xml(attribute(default))]
pub height: Option<u16>,
/// The SHA-1 hash of the image data for the specified content-type.
id: Required<Sha1HexAttribute> = "id",
#[xml(attribute)]
pub id: Sha1HexAttribute,
/// The IANA-registered content type of the image data.
type_: Required<String> = "type",
#[xml(attribute = "type")]
pub type_: String,
/// The http: or https: URL at which the image data file is hosted.
url: Option<String> = "url",
]
);
#[xml(attribute(default))]
pub url: Option<String>,
}
generate_element!(
/// The actual avatar data.

View file

@ -16,9 +16,12 @@
//!
//! The [`Conference`][crate::bookmarks::Conference] struct used in [`private::Query`][`crate::private::Query`] is the one from this module. Only the querying mechanism changes from a legacy PubSub implementation here, to a legacy Private XML Query implementation in that other module. The [`Conference`][crate::bookmarks2::Conference] element from the [`bookmarks2`][crate::bookmarks2] module is a different structure, but conversion is possible from [`bookmarks::Conference`][crate::bookmarks::Conference] to [`bookmarks2::Conference`][crate::bookmarks2::Conference] via the [`Conference::into_bookmarks2`][crate::bookmarks::Conference::into_bookmarks2] method.
use xso::{FromXml, IntoXml};
use jid::BareJid;
pub use crate::bookmarks2::Autojoin;
use crate::ns;
generate_element!(
/// A conference bookmark.
@ -59,17 +62,18 @@ impl Conference {
}
}
generate_element!(
/// An URL bookmark.
Url, "url", BOOKMARKS,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::BOOKMARKS, name = "url")]
pub struct Url {
/// A user-defined name for this URL.
name: Option<String> = "name",
#[xml(attribute(default))]
pub name: Option<String>,
/// The URL of this bookmark.
url: Required<String> = "url",
]
);
#[xml(attribute)]
pub url: String,
}
generate_element!(
/// Container element for multiple bookmarks.

View file

@ -4,24 +4,29 @@
// 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},
FromXml, IntoXml,
};
use crate::data_forms::{DataForm, DataFormType};
use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns;
use crate::rsm::{SetQuery, SetResult};
use crate::Element;
use jid::Jid;
use xso::error::{Error, FromElementError};
generate_element!(
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` element.
///
/// It should only be used in an `<iq type='get'/>`, as it can only represent
/// the request, and not a result.
DiscoInfoQuery, "query", DISCO_INFO,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::DISCO_INFO, name = "query")]
pub struct DiscoInfoQuery {
/// Node on which we are doing the discovery.
node: Option<String> = "node",
]);
#[xml(attribute(default))]
pub node: Option<String>,
}
impl IqGetPayload for DiscoInfoQuery {}
@ -197,17 +202,22 @@ children: [
impl IqGetPayload for DiscoItemsQuery {}
generate_element!(
/// Structure representing an `<item xmlns='http://jabber.org/protocol/disco#items'/>` element.
Item, "item", DISCO_ITEMS,
attributes: [
#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)]
#[xml(namespace = ns::DISCO_ITEMS, name = "item")]
pub struct Item {
/// JID of the entity pointed by this item.
jid: Required<Jid> = "jid",
#[xml(attribute)]
pub jid: Jid,
/// Node of the entity pointed by this item.
node: Option<String> = "node",
#[xml(attribute(default))]
pub node: Option<String>,
/// Name of the entity pointed by this item.
name: Option<String> = "name",
]);
#[xml(attribute(default))]
pub name: Option<String>,
}
generate_element!(
/// Structure representing a `<query

View file

@ -4,20 +4,24 @@
// 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 crate::message::MessagePayload;
use xso::{FromXml, IntoXml};
use crate::message::MessagePayload;
use crate::ns;
generate_element!(
/// Structure representing an `<encryption xmlns='urn:xmpp:eme:0'/>` element.
ExplicitMessageEncryption, "encryption", EME,
attributes: [
#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)]
#[xml(namespace = ns::EME, name = "encryption")]
pub struct ExplicitMessageEncryption {
/// Namespace of the encryption scheme used.
namespace: Required<String> = "namespace",
#[xml(attribute)]
pub namespace: String,
/// User-friendly name for the encryption scheme, should be `None` for OTR,
/// legacy OpenPGP and OX.
name: Option<String> = "name",
]
);
#[xml(attribute(default))]
pub name: Option<String>,
}
impl MessagePayload for ExplicitMessageEncryption {}
@ -69,7 +73,8 @@ mod tests {
#[test]
fn test_invalid_child() {
let elem: Element = "<encryption xmlns='urn:xmpp:eme:0'><coucou/></encryption>"
let elem: Element =
"<encryption xmlns='urn:xmpp:eme:0' namespace='urn:xmpp:otr:0'><coucou/></encryption>"
.parse()
.unwrap();
let error = ExplicitMessageEncryption::try_from(elem).unwrap_err();
@ -77,7 +82,10 @@ mod tests {
FromElementError::Invalid(Error::Other(string)) => string,
_ => panic!(),
};
assert_eq!(message, "Unknown child in encryption element.");
assert_eq!(
message,
"Unknown child in ExplicitMessageEncryption element."
);
}
#[test]

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::{FromXmlText, IntoXmlText};
use crate::util::text_node_codecs::{Base64, Codec};
use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine};
use minidom::IntoAttributeValue;
@ -184,6 +186,18 @@ impl FromStr for Sha1HexAttribute {
}
}
impl FromXmlText for Sha1HexAttribute {
fn from_xml_text(s: String) -> Result<Self, xso::error::Error> {
Self::from_str(&s).map_err(xso::error::Error::text_parse_error)
}
}
impl IntoXmlText for Sha1HexAttribute {
fn into_xml_text(self) -> Result<String, xso::error::Error> {
Ok(self.to_hex())
}
}
impl IntoAttributeValue for Sha1HexAttribute {
fn into_attribute_value(self) -> Option<String> {
Some(self.to_hex())

View file

@ -4,27 +4,31 @@
// 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 xso::{
error::{Error, FromElementError},
FromXml, IntoXml,
};
use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns;
use crate::Element;
use xso::error::{Error, FromElementError};
generate_element!(
/// Requesting a slot
SlotRequest, "request", HTTP_UPLOAD,
attributes: [
#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)]
#[xml(namespace = ns::HTTP_UPLOAD, name = "request")]
pub struct SlotRequest {
/// The filename to be uploaded.
filename: Required<String> = "filename",
#[xml(attribute)]
pub filename: String,
/// Size of the file to be uploaded.
size: Required<u64> = "size",
#[xml(attribute)]
pub size: u64,
/// Content-Type of the file.
content_type: Option<String> = "content-type",
]
);
#[xml(attribute(name = "content-type"))]
pub content_type: Option<String>,
}
impl IqGetPayload for SlotRequest {}

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::iq::IqSetPayload;
use crate::ns;
use crate::util::text_node_codecs::{Base64, Codec};
generate_id!(
@ -59,13 +62,14 @@ Data, "data", IBB,
impl IqSetPayload for Data {}
generate_element!(
/// Close an open stream.
Close, "close", IBB,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::IBB, name = "close")]
pub struct Close {
/// The identifier of the stream to be closed.
sid: Required<StreamId> = "sid",
]);
#[xml(attribute)]
pub sid: StreamId,
}
impl IqSetPayload for Close {}

View file

@ -4,9 +4,13 @@
// 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 crate::jingle_dtls_srtp::Fingerprint;
use std::net::IpAddr;
use xso::{FromXml, IntoXml};
use crate::jingle_dtls_srtp::Fingerprint;
use crate::ns;
generate_element!(
/// Wrapper element for an ICE-UDP transport.
#[derive(Default)]
@ -63,50 +67,61 @@ generate_attribute!(
}
);
generate_element!(
/// A candidate for an ICE-UDP session.
Candidate, "candidate", JINGLE_ICE_UDP,
attributes: [
#[derive(FromXml, IntoXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::JINGLE_ICE_UDP, name = "candidate")]
pub struct Candidate {
/// A Component ID as defined in ICE-CORE.
component: Required<u8> = "component",
#[xml(attribute)]
pub component: u8,
/// A Foundation as defined in ICE-CORE.
foundation: Required<String> = "foundation",
#[xml(attribute)]
pub foundation: String,
/// An index, starting at 0, that enables the parties to keep track of updates to the
/// candidate throughout the life of the session.
generation: Required<u8> = "generation",
#[xml(attribute)]
pub generation: u8,
/// A unique identifier for the candidate.
id: Required<String> = "id",
#[xml(attribute)]
pub id: String,
/// The Internet Protocol (IP) address for the candidate transport mechanism; this can be
/// either an IPv4 address or an IPv6 address.
ip: Required<IpAddr> = "ip",
#[xml(attribute)]
pub ip: IpAddr,
/// The port at the candidate IP address.
port: Required<u16> = "port",
#[xml(attribute)]
pub port: u16,
/// A Priority as defined in ICE-CORE.
priority: Required<u32> = "priority",
#[xml(attribute)]
pub priority: u32,
/// The protocol to be used. The only value defined by this specification is "udp".
protocol: Required<String> = "protocol",
#[xml(attribute)]
pub protocol: String,
/// A related address as defined in ICE-CORE.
rel_addr: Option<IpAddr> = "rel-addr",
#[xml(attribute(default, name = "rel-addr"))]
pub rel_addr: Option<IpAddr>,
/// A related port as defined in ICE-CORE.
rel_port: Option<u16> = "rel-port",
#[xml(attribute(default, name = "rel-port"))]
pub rel_port: Option<u16>,
/// An index, starting at 0, referencing which network this candidate is on for a given
/// peer.
network: Option<u8> = "network",
#[xml(attribute(default))]
pub network: Option<u8>,
/// A Candidate Type as defined in ICE-CORE.
type_: Required<Type> = "type",
]
);
#[xml(attribute(name = "type"))]
pub type_: Type,
}
#[cfg(test)]
mod tests {

View file

@ -4,9 +4,13 @@
// 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 crate::jingle_ice_udp::Type;
use std::net::IpAddr;
use xso::{FromXml, IntoXml};
use crate::jingle_ice_udp::Type;
use crate::ns;
generate_element!(
/// Wrapper element for an raw UDP transport.
#[derive(Default)]
@ -30,31 +34,36 @@ impl Transport {
}
}
generate_element!(
/// A candidate for an ICE-UDP session.
Candidate, "candidate", JINGLE_RAW_UDP,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::JINGLE_RAW_UDP, name = "candidate")]
pub struct Candidate {
/// A Component ID as defined in ICE-CORE.
component: Required<u8> = "component",
#[xml(attribute)]
pub component: u8,
/// An index, starting at 0, that enables the parties to keep track of updates to the
/// candidate throughout the life of the session.
generation: Required<u8> = "generation",
#[xml(attribute)]
pub generation: u8,
/// A unique identifier for the candidate.
id: Required<String> = "id",
#[xml(attribute)]
pub id: String,
/// The Internet Protocol (IP) address for the candidate transport mechanism; this can be
/// either an IPv4 address or an IPv6 address.
ip: Required<IpAddr> = "ip",
#[xml(attribute)]
pub ip: IpAddr,
/// The port at the candidate IP address.
port: Required<u16> = "port",
#[xml(attribute)]
pub port: u16,
/// A Candidate Type as defined in ICE-CORE.
type_: Option<Type> = "type",
]
);
#[xml(attribute(default, name = "type"))]
pub type_: Option<Type>,
}
#[cfg(test)]
mod tests {

View file

@ -4,17 +4,22 @@
// 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!(
use xso::{FromXml, IntoXml};
use crate::ns;
/// Wrapper element for a rtcp-fb.
RtcpFb, "rtcp-fb", JINGLE_RTCP_FB,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::JINGLE_RTCP_FB, name = "rtcp-fb")]
pub struct RtcpFb {
/// Type of this rtcp-fb.
type_: Required<String> = "type",
#[xml(attribute = "type")]
pub type_: String,
/// Subtype of this rtcp-fb, if relevant.
subtype: Option<String> = "subtype",
]
);
#[xml(attribute(default))]
pub subtype: Option<String>,
}
#[cfg(test)]
mod tests {

View file

@ -137,18 +137,19 @@ impl PayloadType {
}
}
generate_element!(
/// Parameter related to a payload.
Parameter, "parameter", JINGLE_RTP,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::JINGLE_RTP, name = "parameter")]
pub struct Parameter {
/// The name of the parameter, from the list at
/// <https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml>
name: Required<String> = "name",
#[xml(attribute)]
pub name: String,
/// The value of this parameter.
value: Required<String> = "value",
]
);
#[xml(attribute)]
pub value: String,
}
#[cfg(test)]
mod tests {

View file

@ -4,6 +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::ns;
generate_element!(
/// Source element for the ssrc SDP attribute.
Source, "source", JINGLE_SSMA,
@ -27,17 +31,18 @@ impl Source {
}
}
generate_element!(
/// Parameter associated with a ssrc.
Parameter, "parameter", JINGLE_SSMA,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::JINGLE_SSMA, name = "parameter")]
pub struct Parameter {
/// The name of the parameter.
name: Required<String> = "name",
#[xml(attribute)]
pub name: String,
/// The optional value of the parameter.
value: Option<String> = "value",
]
);
#[xml(attribute(default))]
pub value: Option<String>,
}
generate_attribute!(
/// From RFC5888, the list of allowed semantics.

View file

@ -107,20 +107,22 @@ generate_element!(
]
);
generate_element!(
/// A subscription element, describing the state of a subscription.
SubscriptionElem, "subscription", PUBSUB_OWNER,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::PUBSUB_OWNER, name = "subscription")]
pub struct SubscriptionElem {
/// The JID affected by this subscription.
jid: Required<Jid> = "jid",
#[xml(attribute)]
pub jid: Jid,
/// The state of the subscription.
subscription: Required<Subscription> = "subscription",
#[xml(attribute)]
pub subscription: Subscription,
/// Subscription unique id.
subid: Option<String> = "subid",
]
);
#[xml(attribute(default))]
pub subid: Option<String>,
}
/// Main payload used to communicate with a PubSubOwner service.
///

View file

@ -4,6 +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},
FromXml, IntoXml,
};
use crate::data_forms::DataForm;
use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
use crate::ns;
@ -12,10 +17,6 @@ use crate::pubsub::{
};
use crate::Element;
use jid::Jid;
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.
@ -220,17 +221,18 @@ impl From<SubscribeOptions> for Element {
}
}
generate_element!(
/// A request to subscribe a JID to a node.
Subscribe, "subscribe", PUBSUB,
attributes: [
#[derive(FromXml, IntoXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::PUBSUB, name = "subscribe")]
pub struct Subscribe {
/// The JID being subscribed.
jid: Required<Jid> = "jid",
#[xml(attribute)]
pub jid: Jid,
/// The node to subscribe to.
node: Option<NodeName> = "node",
]
);
#[xml(attribute)]
pub node: Option<NodeName>,
}
generate_element!(
/// A request for current subscriptions.
@ -267,20 +269,22 @@ generate_element!(
]
);
generate_element!(
/// An unsubscribe request.
Unsubscribe, "unsubscribe", PUBSUB,
attributes: [
#[derive(FromXml, IntoXml, Debug, PartialEq, Clone)]
#[xml(namespace = ns::PUBSUB, name = "unsubscribe")]
pub struct Unsubscribe {
/// The JID affected by this request.
jid: Required<Jid> = "jid",
#[xml(attribute)]
jid: Jid,
/// The node affected by this request.
node: Option<NodeName> = "node",
#[xml(attribute)]
node: Option<NodeName>,
/// The subscription identifier for this subscription.
subid: Option<SubscriptionId> = "subid",
]
);
#[xml(attribute)]
subid: Option<SubscriptionId>,
}
/// Main payload used to communicate with a PubSub service.
///

View file

@ -109,31 +109,33 @@ generate_element!(
#[xml(namespace = ns::SM, name = "r")]
pub struct R;
generate_element!(
/// Requests a stream resumption.
Resume, "resume", SM,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::SM, name = "resume")]
pub struct Resume {
/// The last handled stanza.
h: Required<u32> = "h",
#[xml(attribute)]
pub h: u32,
/// The previous id given by the server on
/// [enabled](struct.Enabled.html).
previd: Required<StreamId> = "previd",
]
);
#[xml(attribute)]
pub previd: StreamId,
}
generate_element!(
/// The response by the server for a successfully resumed stream.
Resumed, "resumed", SM,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::SM, name = "resumed")]
pub struct Resumed {
/// The last handled stanza.
h: Required<u32> = "h",
#[xml(attribute)]
pub h: u32,
/// The previous id given by the server on
/// [enabled](struct.Enabled.html).
previd: Required<StreamId> = "previd",
]
);
#[xml(attribute)]
pub previd: StreamId,
}
// TODO: add support for optional and required.
/// Represents availability of Stream Management in `<stream:features/>`.

View file

@ -4,29 +4,37 @@
// 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 jid::BareJid;
generate_element!(
use crate::ns;
/// The stream opening for client-server communications.
Stream, "stream", STREAM,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::STREAM, name = "stream")]
pub struct Stream {
/// The JID of the entity opening this stream.
from: Option<BareJid> = "from",
#[xml(attribute(default))]
pub from: Option<BareJid>,
/// The JID of the entity receiving this stream opening.
to: Option<BareJid> = "to",
#[xml(attribute(default))]
to: Option<BareJid>,
/// The id of the stream, used for authentication challenges.
id: Option<String> = "id",
#[xml(attribute(default))]
id: Option<String>,
/// The XMPP version used during this stream.
version: Option<String> = "version",
#[xml(attribute(default))]
version: Option<String>,
/// The default human language for all subsequent stanzas, which will
/// be transmitted to other entities for better localisation.
xml_lang: Option<String> = "xml:lang",
]
);
#[xml(attribute(default, name = "xml:lang"))]
xml_lang: Option<String>,
}
impl Stream {
/// Creates a simple client→server `<stream:stream>` element.

View file

@ -125,6 +125,26 @@ macro_rules! generate_attribute {
})
}
}
impl ::xso::FromXmlText for $elem {
fn from_xml_text(s: String) -> Result<$elem, xso::error::Error> {
s.parse().map_err(xso::error::Error::text_parse_error)
}
}
impl ::xso::IntoXmlText for $elem {
fn into_xml_text(self) -> Result<String, xso::error::Error> {
Ok(String::from(match self {
$($elem::$a => $b),+
}))
}
#[allow(unreachable_patterns)]
fn into_optional_xml_text(self) -> Result<Option<String>, xso::error::Error> {
Ok(Some(String::from(match self {
$elem::$default => return Ok(None),
$($elem::$a => $b),+
})))
}
}
impl ::minidom::IntoAttributeValue for $elem {
#[allow(unreachable_patterns)]
fn into_attribute_value(self) -> Option<String> {

View file

@ -4,29 +4,37 @@
// 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 jid::BareJid;
generate_element!(
use crate::ns;
/// The stream opening for WebSocket.
Open, "open", WEBSOCKET,
attributes: [
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
#[xml(namespace = ns::WEBSOCKET, name = "open")]
pub struct Open {
/// The JID of the entity opening this stream.
from: Option<BareJid> = "from",
#[xml(attribute(default))]
pub from: Option<BareJid>,
/// The JID of the entity receiving this stream opening.
to: Option<BareJid> = "to",
#[xml(attribute(default))]
pub to: Option<BareJid>,
/// The id of the stream, used for authentication challenges.
id: Option<String> = "id",
#[xml(attribute(default))]
pub id: Option<String>,
/// The XMPP version used during this stream.
version: Option<String> = "version",
#[xml(attribute(default))]
pub version: Option<String>,
/// The default human language for all subsequent stanzas, which will
/// be transmitted to other entities for better localisation.
xml_lang: Option<String> = "xml:lang",
]
);
#[xml(attribute(default, name = "xml:lang"))]
pub xml_lang: Option<String>,
}
impl Open {
/// Creates a simple client→server `<open/>` element.