From ccf38cdf9b623c916da5e48f307037328b5b65e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Tue, 9 Jul 2024 17:01:42 +0200 Subject: [PATCH] Port everything over to AsXml --- parsers/src/attention.rs | 4 +- parsers/src/avatar.rs | 4 +- parsers/src/blocking.rs | 6 +- parsers/src/bob.rs | 18 +- parsers/src/bookmarks.rs | 4 +- parsers/src/carbons.rs | 8 +- parsers/src/cert_management.rs | 6 +- parsers/src/csi.rs | 8 +- parsers/src/data_forms_validate.rs | 4 +- parsers/src/date.rs | 9 +- parsers/src/delay.rs | 4 +- parsers/src/disco.rs | 8 +- parsers/src/eme.rs | 4 +- parsers/src/extdisco.rs | 4 +- parsers/src/fast.rs | 10 +- parsers/src/hashes.rs | 26 +-- parsers/src/http_upload.rs | 6 +- parsers/src/ibb.rs | 6 +- parsers/src/idle.rs | 4 +- parsers/src/jid_prep.rs | 6 +- parsers/src/jingle_ft.rs | 4 +- parsers/src/jingle_grouping.rs | 4 +- parsers/src/jingle_ice_udp.rs | 4 +- parsers/src/jingle_raw_udp.rs | 4 +- parsers/src/jingle_rtcp_fb.rs | 4 +- parsers/src/jingle_rtp.rs | 6 +- parsers/src/jingle_ssma.rs | 4 +- parsers/src/jingle_thumnails.rs | 4 +- parsers/src/legacy_omemo.rs | 18 +- parsers/src/mam.rs | 8 +- parsers/src/message_correct.rs | 4 +- parsers/src/mix.rs | 10 +- parsers/src/muc/muc.rs | 4 +- parsers/src/muc/user.rs | 4 +- parsers/src/occupant_id.rs | 4 +- parsers/src/openpgp.rs | 6 +- parsers/src/ping.rs | 4 +- parsers/src/pubsub/owner.rs | 10 +- parsers/src/pubsub/pubsub.rs | 10 +- parsers/src/reactions.rs | 4 +- parsers/src/receipts.rs | 6 +- parsers/src/rtt.rs | 6 +- parsers/src/sasl.rs | 12 +- parsers/src/sm.rs | 12 +- parsers/src/stanza_id.rs | 6 +- parsers/src/stream.rs | 4 +- parsers/src/time.rs | 4 +- parsers/src/util/macro_tests.rs | 34 ++-- parsers/src/util/macros.rs | 46 ++--- parsers/src/vcard.rs | 4 +- parsers/src/version.rs | 4 +- parsers/src/websocket.rs | 4 +- xso-proc/Cargo.toml | 2 +- xso-proc/src/compound.rs | 89 ++++++---- xso-proc/src/field.rs | 36 ++-- xso-proc/src/lib.rs | 31 ++-- xso-proc/src/scope.rs | 21 ++- xso-proc/src/state.rs | 69 ++++---- xso-proc/src/structs.rs | 74 ++++++-- xso-proc/src/types.rs | 151 ++++++++++++++-- xso/src/from_xml_doc.md | 10 +- xso/src/lib.rs | 30 +++- xso/src/rxml_util.rs | 267 ++++++++++++++++++++++++++++- xso/src/text.rs | 28 +-- 64 files changed, 848 insertions(+), 371 deletions(-) diff --git a/parsers/src/attention.rs b/parsers/src/attention.rs index 66933778636c481c30cbf2bc1f4912d10f57bcd3..c94bd54c8e7ea3c09d1ca9aded149edb8e6a37bd 100644 --- a/parsers/src/attention.rs +++ b/parsers/src/attention.rs @@ -3,13 +3,13 @@ // This Source Code Form is subject to the terms of the Mozilla Public // 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::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; /// Requests the attention of the recipient. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::ATTENTION, name = "attention")] pub struct Attention; diff --git a/parsers/src/avatar.rs b/parsers/src/avatar.rs index 4fd873e272c9f8eae3a95c89841217dbb7e3d9c3..b4af78a3cf0e3b7108c2fc61af7ca8931d97df76 100644 --- a/parsers/src/avatar.rs +++ b/parsers/src/avatar.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::hashes::Sha1HexAttribute; use crate::ns; @@ -23,7 +23,7 @@ generate_element!( impl PubSubPayload for Metadata {} /// Communicates avatar metadata. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::AVATAR_METADATA, name = "info")] pub struct Info { /// The size of the image data in bytes. diff --git a/parsers/src/blocking.rs b/parsers/src/blocking.rs index 5f0aa9c77e26dfcf104f7ebff5111c7430851345..223426a7a197c62223a179a262f5b2d6871edb7e 100644 --- a/parsers/src/blocking.rs +++ b/parsers/src/blocking.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; @@ -14,7 +14,7 @@ use xso::error::FromElementError; /// The element requesting the blocklist, the result iq will contain a /// [BlocklistResult]. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::BLOCKING, name = "blocklist")] pub struct BlocklistRequest; @@ -88,7 +88,7 @@ generate_blocking_element!( impl IqSetPayload for Unblock {} /// The application-specific error condition when a message is blocked. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::BLOCKING_ERRORS, name = "blocked")] pub struct Blocked; diff --git a/parsers/src/bob.rs b/parsers/src/bob.rs index b2317df54613af177ea0e15e60031c89c96f0ed7..b2221e2c2e347c3e132b1bb9747bb75f150eae72 100644 --- a/parsers/src/bob.rs +++ b/parsers/src/bob.rs @@ -4,12 +4,14 @@ // 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, text::Base64, FromXml, FromXmlText, IntoXml, IntoXmlText}; +use std::borrow::Cow; +use std::str::FromStr; + +use xso::{error::Error, text::Base64, AsXml, AsXmlText, FromXml, FromXmlText}; use crate::hashes::{Algo, Hash}; use crate::ns; use minidom::IntoAttributeValue; -use std::str::FromStr; /// A Content-ID, as defined in RFC2111. /// @@ -56,14 +58,18 @@ impl FromXmlText for ContentId { } } -impl IntoXmlText for ContentId { - fn into_xml_text(self) -> Result { +impl AsXmlText for ContentId { + fn as_xml_text(&self) -> Result, Error> { let algo = match self.hash.algo { Algo::Sha_1 => "sha1", Algo::Sha_256 => "sha256", _ => unimplemented!(), }; - Ok(format!("{}+{}@bob.xmpp.org", algo, self.hash.to_hex())) + Ok(Cow::Owned(format!( + "{}+{}@bob.xmpp.org", + algo, + self.hash.to_hex() + ))) } } @@ -79,7 +85,7 @@ impl IntoAttributeValue for ContentId { } /// Request for an uncached cid file. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::BOB, name = "data")] pub struct Data { /// The cid in question. diff --git a/parsers/src/bookmarks.rs b/parsers/src/bookmarks.rs index a814e574090cb562575109cb887e59217d2263a7..aa92444208de14ac827ce677b07bb93cef76e13d 100644 --- a/parsers/src/bookmarks.rs +++ b/parsers/src/bookmarks.rs @@ -16,7 +16,7 @@ //! //! 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 xso::{AsXml, FromXml}; use jid::BareJid; @@ -63,7 +63,7 @@ impl Conference { } /// An URL bookmark. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::BOOKMARKS, name = "url")] pub struct Url { /// A user-defined name for this URL. diff --git a/parsers/src/carbons.rs b/parsers/src/carbons.rs index a3e5a3f64bdf9a4e8e21cb1f514d97c197e85c03..9fdcc88f09dda5fa21fa3348ce4a11aad4dffdb7 100644 --- a/parsers/src/carbons.rs +++ b/parsers/src/carbons.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::forwarding::Forwarded; use crate::iq::IqSetPayload; @@ -12,14 +12,14 @@ use crate::message::MessagePayload; use crate::ns; /// Enable carbons for this session. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::CARBONS, name = "enable")] pub struct Enable; impl IqSetPayload for Enable {} /// Disable a previously-enabled carbons. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::CARBONS, name = "disable")] pub struct Disable; @@ -27,7 +27,7 @@ impl IqSetPayload for Disable {} /// Request the enclosing message to not be copied to other carbons-enabled /// resources of the user. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::CARBONS, name = "private")] pub struct Private; diff --git a/parsers/src/cert_management.rs b/parsers/src/cert_management.rs index f40d450e3068e8744954aa04eded65e42e1d94c9..4e3fb87a8720694ec0a45be23efc6e4032d4a4f8 100644 --- a/parsers/src/cert_management.rs +++ b/parsers/src/cert_management.rs @@ -4,7 +4,7 @@ // 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::{text::Base64, FromXml, IntoXml}; +use xso::{text::Base64, AsXml, FromXml}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::ns; @@ -17,7 +17,7 @@ generate_elem_id!( ); /// An X.509 certificate. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL_CERT, name = "x509cert")] pub struct Cert { /// The BER X.509 data. @@ -43,7 +43,7 @@ generate_element!( impl IqSetPayload for Append {} /// Client requests the current list of X.509 certificates. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL_CERT, name = "items")] pub struct ListCertsQuery; diff --git a/parsers/src/csi.rs b/parsers/src/csi.rs index 6693bd2e622423753699fa6c2142ef1735fb541f..56cb76af7656b060c90ef16015c5f9db50b0721e 100644 --- a/parsers/src/csi.rs +++ b/parsers/src/csi.rs @@ -4,22 +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/. -use xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::ns; /// Stream:feature sent by the server to advertise it supports CSI. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::CSI, name = "csi")] pub struct Feature; /// Client indicates it is inactive. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::CSI, name = "inactive")] pub struct Inactive; /// Client indicates it is active again. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::CSI, name = "active")] pub struct Active; diff --git a/parsers/src/data_forms_validate.rs b/parsers/src/data_forms_validate.rs index d2fa42f59811b83a2e1dc977f278ed67fdf3c8d4..f9aad49f2aceeed3c4c99537d5117ddc4f2ddf42 100644 --- a/parsers/src/data_forms_validate.rs +++ b/parsers/src/data_forms_validate.rs @@ -8,7 +8,7 @@ use std::fmt::{Display, Formatter}; use std::str::FromStr; use minidom::{Element, IntoAttributeValue}; -use xso::{error::FromElementError, FromXml, IntoXml}; +use xso::{error::FromElementError, AsXml, FromXml}; use crate::ns::{self, XDATA_VALIDATE}; use crate::Error; @@ -67,7 +67,7 @@ pub enum Method { } /// Selection Ranges in "list-multi" -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::XDATA_VALIDATE, name = "list-range")] pub struct ListRange { /// The 'min' attribute specifies the minimum allowable number of selected/entered values. diff --git a/parsers/src/date.rs b/parsers/src/date.rs index 277fc0d940ea2fe2f8de7e369be21c0226c068ae..8ac2499457194363d5858bc586c1aff139aa3ae2 100644 --- a/parsers/src/date.rs +++ b/parsers/src/date.rs @@ -4,9 +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 std::borrow::Cow; use std::str::FromStr; -use xso::{error::Error, FromXmlText, IntoXmlText}; +use xso::{error::Error, AsXmlText, FromXmlText}; use chrono::{DateTime as ChronoDateTime, FixedOffset}; use minidom::{IntoAttributeValue, Node}; @@ -48,9 +49,9 @@ impl FromXmlText for DateTime { } } -impl IntoXmlText for DateTime { - fn into_xml_text(self) -> Result { - Ok(self.0.to_rfc3339()) +impl AsXmlText for DateTime { + fn as_xml_text(&self) -> Result, Error> { + Ok(Cow::Owned(self.0.to_rfc3339())) } } diff --git a/parsers/src/delay.rs b/parsers/src/delay.rs index 869b181aee3c8f369102407cb38e38950b3c8bd0..da946dec76af8d5f546fa4d0a4de3f1b5f913ceb 100644 --- a/parsers/src/delay.rs +++ b/parsers/src/delay.rs @@ -4,7 +4,7 @@ // 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::{text::EmptyAsNone, FromXml, IntoXml}; +use xso::{text::EmptyAsNone, AsXml, FromXml}; use crate::date::DateTime; use crate::message::MessagePayload; @@ -13,7 +13,7 @@ use crate::presence::PresencePayload; use jid::Jid; /// Notes when and by whom a message got stored for later delivery. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::DELAY, name = "delay")] pub struct Delay { /// The entity which delayed this message. diff --git a/parsers/src/disco.rs b/parsers/src/disco.rs index 45455f9792b9a967dbadbc39f32fad8cd456d5ef..c9734208c86101a6cc0a5d11921287182ee6eb98 100644 --- a/parsers/src/disco.rs +++ b/parsers/src/disco.rs @@ -6,7 +6,7 @@ use xso::{ error::{Error, FromElementError}, - FromXml, IntoXml, + AsXml, FromXml, }; use crate::data_forms::{DataForm, DataFormType}; @@ -20,7 +20,7 @@ use jid::Jid; /// /// It should only be used in an ``, as it can only represent /// the request, and not a result. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::DISCO_INFO, name = "query")] pub struct DiscoInfoQuery { /// Node on which we are doing the discovery. @@ -31,7 +31,7 @@ pub struct DiscoInfoQuery { impl IqGetPayload for DiscoInfoQuery {} /// Structure representing a `` element. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq, Eq, Hash)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq, Eq, Hash)] #[xml(namespace = ns::DISCO_INFO, name = "feature")] pub struct Feature { /// Namespace of the feature we want to represent. @@ -203,7 +203,7 @@ children: [ impl IqGetPayload for DiscoItemsQuery {} /// Structure representing an `` element. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::DISCO_ITEMS, name = "item")] pub struct Item { /// JID of the entity pointed by this item. diff --git a/parsers/src/eme.rs b/parsers/src/eme.rs index 9dbea40cde1fe778596b19621a22920fa456e3f0..e9c5319e42abc3d255f7597b2d22a4213c98607c 100644 --- a/parsers/src/eme.rs +++ b/parsers/src/eme.rs @@ -4,13 +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 xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; /// Structure representing an `` element. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::EME, name = "encryption")] pub struct ExplicitMessageEncryption { /// Namespace of the encryption scheme used. diff --git a/parsers/src/extdisco.rs b/parsers/src/extdisco.rs index 264812df0b7781b43f9ac7b6ef6b0707832b4bc2..48532a491a9caf23e0909181f71c8ed3008a40fe 100644 --- a/parsers/src/extdisco.rs +++ b/parsers/src/extdisco.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::data_forms::DataForm; use crate::date::DateTime; @@ -101,7 +101,7 @@ generate_element!( impl IqGetPayload for Service {} /// Structure representing a `` element. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::EXT_DISCO, name = "services")] pub struct ServicesQuery { /// TODO diff --git a/parsers/src/fast.rs b/parsers/src/fast.rs index e287cf9880ff36125e14a50737bdc6d87cc6c88e..a96843f9d29b226c2d41c5878cf9b25eac052d52 100644 --- a/parsers/src/fast.rs +++ b/parsers/src/fast.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::date::DateTime; use crate::ns; @@ -14,7 +14,7 @@ generate_elem_id!( Mechanism, "mechanism", FAST ); -// TODO: Replace this with a proper bool once we can derive FromXml and IntoXml on FastQuery. +// TODO: Replace this with a proper bool once we can derive FromXml and AsXml on FastQuery. generate_attribute!( /// Whether TLS zero-roundtrip is possible. Tls0Rtt, "tls-0rtt", bool @@ -34,7 +34,7 @@ children: [ ); /// This is the `` element the client MUST include within its SASL2 authentication request. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::FAST, name = "fast")] pub struct FastResponse { /// Servers MUST reject any authentication requests received via TLS 0-RTT payloads that do not @@ -51,7 +51,7 @@ pub struct FastResponse { } /// This is the `` element sent by the client in the SASL2 authenticate step. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::FAST, name = "request-token")] pub struct RequestToken { /// This element MUST contain a 'mechanism' attribute, the value of which MUST be one of the @@ -62,7 +62,7 @@ pub struct RequestToken { /// This is the `` element sent by the server on successful SASL2 authentication containing /// a `` element. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::FAST, name = "token")] pub struct Token { /// The secret token to be used for subsequent authentications, as generated by the server. diff --git a/parsers/src/hashes.rs b/parsers/src/hashes.rs index 7585af715d90b24b2755c5a9d43c0c011f11fb47..d4ce6d8a5fd75dd6c4b7a3f0f0167f3a9c9e3e52 100644 --- a/parsers/src/hashes.rs +++ b/parsers/src/hashes.rs @@ -4,14 +4,16 @@ // 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, text::Base64, FromXml, FromXmlText, IntoXml, IntoXmlText}; - -use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; -use minidom::IntoAttributeValue; +use std::borrow::Cow; use std::num::ParseIntError; use std::ops::{Deref, DerefMut}; use std::str::FromStr; +use xso::{error::Error, text::Base64, AsXml, AsXmlText, FromXml, FromXmlText}; + +use base64::{engine::general_purpose::STANDARD as Base64Engine, Engine}; +use minidom::IntoAttributeValue; + use crate::ns; /// List of the algorithms we support, or Unknown. @@ -97,9 +99,9 @@ impl FromXmlText for Algo { } } -impl IntoXmlText for Algo { - fn into_xml_text(self) -> Result { - Ok(String::from(match self { +impl AsXmlText for Algo { + fn as_xml_text(&self) -> Result, Error> { + Ok(Cow::Borrowed(match self { Algo::Sha_1 => "sha-1", Algo::Sha_256 => "sha-256", Algo::Sha_512 => "sha-512", @@ -107,7 +109,7 @@ impl IntoXmlText for Algo { Algo::Sha3_512 => "sha3-512", Algo::Blake2b_256 => "blake2b-256", Algo::Blake2b_512 => "blake2b-512", - Algo::Unknown(text) => return Ok(text), + Algo::Unknown(text) => text.as_str(), })) } } @@ -120,7 +122,7 @@ impl IntoAttributeValue for Algo { /// This element represents a hash of some data, defined by the hash /// algorithm used and the computed value. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::HASHES, name = "hash")] pub struct Hash { /// The algorithm used to create this hash. @@ -213,9 +215,9 @@ impl FromXmlText for Sha1HexAttribute { } } -impl IntoXmlText for Sha1HexAttribute { - fn into_xml_text(self) -> Result { - Ok(self.to_hex()) +impl AsXmlText for Sha1HexAttribute { + fn as_xml_text(&self) -> Result, xso::error::Error> { + Ok(Cow::Owned(self.to_hex())) } } diff --git a/parsers/src/http_upload.rs b/parsers/src/http_upload.rs index 6e0d3d95a48fe594d95ccf80a9d3b36906fb0d88..4856e0585160c8f2028515b6c0b5dc75d086a668 100644 --- a/parsers/src/http_upload.rs +++ b/parsers/src/http_upload.rs @@ -6,7 +6,7 @@ use xso::{ error::{Error, FromElementError}, - FromXml, IntoXml, + AsXml, FromXml, }; use crate::iq::{IqGetPayload, IqResultPayload}; @@ -14,7 +14,7 @@ use crate::ns; use crate::Element; /// Requesting a slot -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::HTTP_UPLOAD, name = "request")] pub struct SlotRequest { /// The filename to be uploaded. @@ -97,7 +97,7 @@ generate_element!( ); /// Get URL -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::HTTP_UPLOAD, name = "get")] pub struct Get { /// URL diff --git a/parsers/src/ibb.rs b/parsers/src/ibb.rs index 7e937f6ef0e30922845c5a8455cd20a6ec3971b6..fe5f6dd6b7f23049a14446143f37ff0453edde30 100644 --- a/parsers/src/ibb.rs +++ b/parsers/src/ibb.rs @@ -4,7 +4,7 @@ // 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::{text::Base64, FromXml, IntoXml}; +use xso::{text::Base64, AsXml, FromXml}; use crate::iq::IqSetPayload; use crate::ns; @@ -44,7 +44,7 @@ attributes: [ impl IqSetPayload for Open {} /// Exchange a chunk of data in an open stream. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::IBB, name = "data")] pub struct Data { /// Sequence number of this chunk, must wraparound after 65535. @@ -63,7 +63,7 @@ pub struct Data { impl IqSetPayload for Data {} /// Close an open stream. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::IBB, name = "close")] pub struct Close { /// The identifier of the stream to be closed. diff --git a/parsers/src/idle.rs b/parsers/src/idle.rs index 866e3e350cc0f38f319dbf7dd650cc86b0dd4e39..dfee8ec3afee1dc5d67d8ff60cd2f58a404a0089 100644 --- a/parsers/src/idle.rs +++ b/parsers/src/idle.rs @@ -4,14 +4,14 @@ // 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::{AsXml, FromXml}; use crate::date::DateTime; use crate::ns; use crate::presence::PresencePayload; /// Represents the last time the user interacted with their system. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::IDLE, name = "idle")] pub struct Idle { /// The time at which the user stopped interacting. diff --git a/parsers/src/jid_prep.rs b/parsers/src/jid_prep.rs index 97a66dc02bda5f328346f2c55cbd0a1794eb09a3..82e1d8bf11c01b465b79af550ce90b76e2447ed3 100644 --- a/parsers/src/jid_prep.rs +++ b/parsers/src/jid_prep.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use jid::Jid; @@ -12,7 +12,7 @@ use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; /// Request from a client to stringprep/PRECIS a string into a JID. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JID_PREP, name = "jid")] pub struct JidPrepQuery { /// The potential JID. @@ -30,7 +30,7 @@ impl JidPrepQuery { } /// Response from the server with the stringprep’d/PRECIS’d JID. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JID_PREP, name = "jid")] pub struct JidPrepResponse { /// The JID. diff --git a/parsers/src/jingle_ft.rs b/parsers/src/jingle_ft.rs index 64dcca06975ca55e24699cbd39f36d5cb5dee3c9..bedca4dadc453226b9ca796a345e51af1c5913e3 100644 --- a/parsers/src/jingle_ft.rs +++ b/parsers/src/jingle_ft.rs @@ -13,7 +13,7 @@ use std::collections::BTreeMap; use std::str::FromStr; use xso::{ error::{Error, FromElementError}, - FromXml, IntoXml, + AsXml, FromXml, }; generate_element!( @@ -323,7 +323,7 @@ impl From for Element { } /// A notice that the file transfer has been completed. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_FT, name = "received")] pub struct Received { /// The content identifier of this Jingle session. diff --git a/parsers/src/jingle_grouping.rs b/parsers/src/jingle_grouping.rs index 082ab211440b7a64d43d3658973bf08dd3428988..a01d3f5fbc34a665b90f8a747a5dfc78e885d2bc 100644 --- a/parsers/src/jingle_grouping.rs +++ b/parsers/src/jingle_grouping.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::jingle::ContentId; use crate::ns; @@ -21,7 +21,7 @@ generate_attribute!( ); /// Describes a content that should be grouped with other ones. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_GROUPING, name = "content")] pub struct Content { /// The name of the matching [`Content`](crate::jingle::Content). diff --git a/parsers/src/jingle_ice_udp.rs b/parsers/src/jingle_ice_udp.rs index bc1182bfd9f6a132d5dfbf6caa8828f346c38482..32efac692c4b9df8c91803d08c0704073b6efa28 100644 --- a/parsers/src/jingle_ice_udp.rs +++ b/parsers/src/jingle_ice_udp.rs @@ -6,7 +6,7 @@ use std::net::IpAddr; -use xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::jingle_dtls_srtp::Fingerprint; use crate::ns; @@ -68,7 +68,7 @@ generate_attribute!( ); /// A candidate for an ICE-UDP session. -#[derive(FromXml, IntoXml, Debug, PartialEq, Clone)] +#[derive(FromXml, AsXml, Debug, PartialEq, Clone)] #[xml(namespace = ns::JINGLE_ICE_UDP, name = "candidate")] pub struct Candidate { /// A Component ID as defined in ICE-CORE. diff --git a/parsers/src/jingle_raw_udp.rs b/parsers/src/jingle_raw_udp.rs index c7e36a30a6125d2f123104c131a4211c262375de..78e0bc04458cff3ac3d6d0f8a7a1085295420149 100644 --- a/parsers/src/jingle_raw_udp.rs +++ b/parsers/src/jingle_raw_udp.rs @@ -6,7 +6,7 @@ use std::net::IpAddr; -use xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::jingle_ice_udp::Type; use crate::ns; @@ -35,7 +35,7 @@ impl Transport { } /// A candidate for an ICE-UDP session. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_RAW_UDP, name = "candidate")] pub struct Candidate { /// A Component ID as defined in ICE-CORE. diff --git a/parsers/src/jingle_rtcp_fb.rs b/parsers/src/jingle_rtcp_fb.rs index 052fc3451c8cdd3f9e6e9b79a4c7f1a8346a812d..1ff9f8223ce603e65490892b749d8c7a7cce7c5d 100644 --- a/parsers/src/jingle_rtcp_fb.rs +++ b/parsers/src/jingle_rtcp_fb.rs @@ -4,12 +4,12 @@ // 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::{AsXml, FromXml}; use crate::ns; /// Wrapper element for a rtcp-fb. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_RTCP_FB, name = "rtcp-fb")] pub struct RtcpFb { /// Type of this rtcp-fb. diff --git a/parsers/src/jingle_rtp.rs b/parsers/src/jingle_rtp.rs index b35d91f8b1b4e123bf0b1e124a6a9fadb1293593..0442a16426e85d5e0b2f4caf3254fdaed08bdaf2 100644 --- a/parsers/src/jingle_rtp.rs +++ b/parsers/src/jingle_rtp.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::jingle_rtcp_fb::RtcpFb; use crate::jingle_rtp_hdrext::RtpHdrext; @@ -13,7 +13,7 @@ use crate::ns; /// Specifies the ability to multiplex RTP Data and Control Packets on a single port as /// described in RFC 5761. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_RTP, name = "rtcp-mux")] pub struct RtcpMux; @@ -138,7 +138,7 @@ impl PayloadType { } /// Parameter related to a payload. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_RTP, name = "parameter")] pub struct Parameter { /// The name of the parameter, from the list at diff --git a/parsers/src/jingle_ssma.rs b/parsers/src/jingle_ssma.rs index f221f6b1140699edcfe40c481aa8061806a9c181..df43f550ff8a57e3f3ebbde978cc656590a79718 100644 --- a/parsers/src/jingle_ssma.rs +++ b/parsers/src/jingle_ssma.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::ns; @@ -32,7 +32,7 @@ impl Source { } /// Parameter associated with a ssrc. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_SSMA, name = "parameter")] pub struct Parameter { /// The name of the parameter. diff --git a/parsers/src/jingle_thumnails.rs b/parsers/src/jingle_thumnails.rs index 59b5770ee176c5478322c04879cf4c67d62aa402..59de078a70a9c96b939ce52eae1f402a23706e42 100644 --- a/parsers/src/jingle_thumnails.rs +++ b/parsers/src/jingle_thumnails.rs @@ -6,12 +6,12 @@ // 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::{AsXml, FromXml}; use crate::ns; /// A Jingle thumbnail. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::JINGLE_THUMBNAILS, name = "thumbnail")] pub struct Thumbnail { /// The URI of the thumbnail. diff --git a/parsers/src/legacy_omemo.rs b/parsers/src/legacy_omemo.rs index 4260c0c15aaf1bfa9687c4db5308df8dd775f283..2fe46bf3c8681311b33f340da7dcb9890164b4af 100644 --- a/parsers/src/legacy_omemo.rs +++ b/parsers/src/legacy_omemo.rs @@ -4,14 +4,14 @@ // 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::{text::Base64, FromXml, IntoXml}; +use xso::{text::Base64, AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; use crate::pubsub::PubSubPayload; /// Element of the device list -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "device")] pub struct Device { /// Device id @@ -33,7 +33,7 @@ impl PubSubPayload for DeviceList {} /// SignedPreKey public key /// Part of a device's bundle -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeyPublic")] pub struct SignedPreKeyPublic { /// SignedPreKey id @@ -47,7 +47,7 @@ pub struct SignedPreKeyPublic { /// SignedPreKey signature /// Part of a device's bundle -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeySignature")] pub struct SignedPreKeySignature { /// Signature bytes @@ -56,7 +56,7 @@ pub struct SignedPreKeySignature { } /// Part of a device's bundle -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "identityKey")] pub struct IdentityKey { /// Serialized PublicKey @@ -76,7 +76,7 @@ generate_element!( /// PreKey public key /// Part of a device's bundle -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "preKeyPublic")] pub struct PreKeyPublic { /// PreKey id @@ -123,7 +123,7 @@ generate_element!( ); /// IV used for payload encryption -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "iv")] pub struct IV { /// IV bytes @@ -139,7 +139,7 @@ generate_attribute!( ); /// Part of the OMEMO element header -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "key")] pub struct Key { /// The device id this key is encrypted for. @@ -159,7 +159,7 @@ pub struct Key { } /// The encrypted message body -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::LEGACY_OMEMO, name = "payload")] pub struct Payload { /// Encrypted with AES-128 in Galois/Counter Mode (GCM) diff --git a/parsers/src/mam.rs b/parsers/src/mam.rs index 9946ce91a1b58ef2072dfeb1bbc4136a775606ec..9482b92a9343acb01d3295e95d1505ef64b0526a 100644 --- a/parsers/src/mam.rs +++ b/parsers/src/mam.rs @@ -6,7 +6,7 @@ use xso::{ error::{Error, FromElementError}, - FromXml, IntoXml, + AsXml, FromXml, }; use crate::data_forms::DataForm; @@ -165,7 +165,7 @@ generate_element!( impl IqResultPayload for Fin {} /// Metadata of the first message in the archive. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::MAM, name = "start")] pub struct Start { /// The id of the first message in the archive. @@ -178,7 +178,7 @@ pub struct Start { } /// Metadata of the last message in the archive. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::MAM, name = "end")] pub struct End { /// The id of the last message in the archive. @@ -191,7 +191,7 @@ pub struct End { } /// Request an archive for its metadata. -#[derive(FromXml, IntoXml, Debug, Clone, PartialEq)] +#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[xml(namespace = ns::MAM, name = "metadata")] pub struct MetadataQuery; diff --git a/parsers/src/message_correct.rs b/parsers/src/message_correct.rs index 20d0dd3a310a28658480a97d687acccf1afeed55..1516053ade7832ec25db97ba1f22dda7424bbf3d 100644 --- a/parsers/src/message_correct.rs +++ b/parsers/src/message_correct.rs @@ -4,14 +4,14 @@ // 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::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; /// Defines that the message containing this payload should replace a /// previous message, identified by the id. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::MESSAGE_CORRECT, name = "replace")] pub struct Replace { /// The 'id' attribute of the message getting corrected. diff --git a/parsers/src/mix.rs b/parsers/src/mix.rs index 064532452fb433d1cddea1e974fe9c05bdc5eeda..87758a60b58afe6fbff24f2883d1c5a4493db504 100644 --- a/parsers/src/mix.rs +++ b/parsers/src/mix.rs @@ -7,7 +7,7 @@ // TODO: validate nicks by applying the “nickname” profile of the PRECIS OpaqueString class, as // defined in RFC 7700. -use xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::iq::{IqResultPayload, IqSetPayload}; use crate::message::MessagePayload; @@ -59,7 +59,7 @@ impl Participant { } /// A node to subscribe to. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::MIX_CORE, name = "subscribe")] pub struct Subscribe { /// The PubSub node to subscribe to. @@ -151,7 +151,7 @@ impl UpdateSubscription { /// Request to leave a given MIX channel. It will automatically unsubscribe the user from all /// nodes on this channel. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::MIX_CORE, name = "leave")] pub struct Leave; @@ -204,7 +204,7 @@ impl Mix { } /// Create a new MIX channel. -#[derive(FromXml, IntoXml, PartialEq, Clone, Debug, Default)] +#[derive(FromXml, AsXml, PartialEq, Clone, Debug, Default)] #[xml(namespace = ns::MIX_CORE, name = "create")] pub struct Create { /// The requested channel identifier. @@ -230,7 +230,7 @@ impl Create { } /// Destroy a given MIX channel. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::MIX_CORE, name = "destroy")] pub struct Destroy { /// The channel identifier to be destroyed. diff --git a/parsers/src/muc/muc.rs b/parsers/src/muc/muc.rs index c5bde638cc82f128c21d1af4ec69b4ff6e355f4c..38fa8a006607bd9c78b2a9c09c01416d5b78a355 100644 --- a/parsers/src/muc/muc.rs +++ b/parsers/src/muc/muc.rs @@ -5,14 +5,14 @@ // 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::{AsXml, FromXml}; use crate::date::DateTime; use crate::ns; use crate::presence::PresencePayload; /// Represents the query for messages before our join. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone, Default)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone, Default)] #[xml(namespace = ns::MUC, name = "history")] pub struct History { /// How many characters of history to send, in XML characters. diff --git a/parsers/src/muc/user.rs b/parsers/src/muc/user.rs index 22a77e368246f90e261ca17cd46b860960bed69e..1cc35335459116ec928855e06a60883a08843766 100644 --- a/parsers/src/muc/user.rs +++ b/parsers/src/muc/user.rs @@ -7,7 +7,7 @@ use xso::{ error::{Error, FromElementError}, - FromXml, IntoXml, + AsXml, FromXml, }; use crate::message::MessagePayload; @@ -131,7 +131,7 @@ impl From for Element { /// Used to continue a one-to-one discussion in a room, with more than one /// participant. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::MUC_USER, name = "continue")] pub struct Continue { /// The thread to continue in this room. diff --git a/parsers/src/occupant_id.rs b/parsers/src/occupant_id.rs index 44780fc6dbc7b5fb2f5fbcb609323b163a37470e..95d9a844cc412acba79609058e67e87fa8156603 100644 --- a/parsers/src/occupant_id.rs +++ b/parsers/src/occupant_id.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; @@ -14,7 +14,7 @@ use crate::presence::PresencePayload; /// /// It allows clients to identify a MUC participant across reconnects and /// renames. It thus prevents impersonification of anonymous users. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::OID, name = "occupant-id")] pub struct OccupantId { /// The id associated to the sending user by the MUC service. diff --git a/parsers/src/openpgp.rs b/parsers/src/openpgp.rs index d6c521bc6d4afded0eed52c08c16de4032d4b59d..fecb97e8c18644c082b030ef80859e0f08744661 100644 --- a/parsers/src/openpgp.rs +++ b/parsers/src/openpgp.rs @@ -4,7 +4,7 @@ // 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::{text::Base64, FromXml, IntoXml}; +use xso::{text::Base64, AsXml, FromXml}; use crate::date::DateTime; use crate::ns; @@ -12,7 +12,7 @@ use crate::pubsub::PubSubPayload; /// Data contained in the PubKey element // TODO: Merge this container with the PubKey struct -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::OX, name = "data")] pub struct PubKeyData { /// Base64 data @@ -36,7 +36,7 @@ generate_element!( impl PubSubPayload for PubKey {} /// Public key metadata -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::OX, name = "pubkey-metadata")] pub struct PubKeyMeta { /// OpenPGP v4 fingerprint diff --git a/parsers/src/ping.rs b/parsers/src/ping.rs index d71514d65135e99297dc43522752a34958972cd8..2d505cc89690c5b9d942ce52950e2be2438f4177 100644 --- a/parsers/src/ping.rs +++ b/parsers/src/ping.rs @@ -5,14 +5,14 @@ // 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::{AsXml, FromXml}; use crate::iq::IqGetPayload; use crate::ns; /// Represents a ping to the recipient, which must be answered with an /// empty `` or with an error. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PING, name = "ping")] pub struct Ping; diff --git a/parsers/src/pubsub/owner.rs b/parsers/src/pubsub/owner.rs index a144ff09cb4c9a09aaf1439b3f898566f1bfbd51..430bae8103f6d059cd116310a0f8fad6bd157e7f 100644 --- a/parsers/src/pubsub/owner.rs +++ b/parsers/src/pubsub/owner.rs @@ -5,7 +5,7 @@ // 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::{AsXml, FromXml}; use crate::data_forms::DataForm; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; @@ -29,7 +29,7 @@ generate_element!( ); /// An affiliation element. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PUBSUB_OWNER, name = "affiliation")] pub struct Affiliation { /// The node this affiliation pertains to. @@ -77,7 +77,7 @@ generate_element!( ); /// A redirect element. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PUBSUB_OWNER, name = "redirect")] pub struct Redirect { /// The node this node will be redirected to. @@ -86,7 +86,7 @@ pub struct Redirect { } /// Request to clear a node. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PUBSUB_OWNER, name = "purge")] pub struct Purge { /// The node to be cleared. @@ -108,7 +108,7 @@ generate_element!( ); /// A subscription element, describing the state of a subscription. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PUBSUB_OWNER, name = "subscription")] pub struct SubscriptionElem { /// The JID affected by this subscription. diff --git a/parsers/src/pubsub/pubsub.rs b/parsers/src/pubsub/pubsub.rs index 9d32cab098bd371efc917568441e9b3da381d3f2..b6cc74e60dbbd98d4ed59f9cb76e9256227a854a 100644 --- a/parsers/src/pubsub/pubsub.rs +++ b/parsers/src/pubsub/pubsub.rs @@ -6,7 +6,7 @@ use xso::{ error::{Error, FromElementError}, - FromXml, IntoXml, + AsXml, FromXml, }; use crate::data_forms::DataForm; @@ -34,7 +34,7 @@ generate_element!( ); /// An affiliation element. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PUBSUB, name = "affiliation")] pub struct Affiliation { /// The node this affiliation pertains to. @@ -56,7 +56,7 @@ generate_element!( ); /// Request to create a new node. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::PUBSUB, name = "create")] pub struct Create { /// The node name to create, if `None` the service will generate one. @@ -222,7 +222,7 @@ impl From for Element { } /// A request to subscribe a JID to a node. -#[derive(FromXml, IntoXml, Debug, PartialEq, Clone)] +#[derive(FromXml, AsXml, Debug, PartialEq, Clone)] #[xml(namespace = ns::PUBSUB, name = "subscribe")] pub struct Subscribe { /// The JID being subscribed. @@ -270,7 +270,7 @@ generate_element!( ); /// An unsubscribe request. -#[derive(FromXml, IntoXml, Debug, PartialEq, Clone)] +#[derive(FromXml, AsXml, Debug, PartialEq, Clone)] #[xml(namespace = ns::PUBSUB, name = "unsubscribe")] pub struct Unsubscribe { /// The JID affected by this request. diff --git a/parsers/src/reactions.rs b/parsers/src/reactions.rs index 90806014f67824476ccbf5c933479aa07ad6fdd8..967b01f61cfb52811a975d8c2d89ac35cefb504f 100644 --- a/parsers/src/reactions.rs +++ b/parsers/src/reactions.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; @@ -25,7 +25,7 @@ generate_element!( impl MessagePayload for Reactions {} /// One emoji reaction. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::REACTIONS, name = "reaction")] pub struct Reaction { /// The text of this reaction. diff --git a/parsers/src/receipts.rs b/parsers/src/receipts.rs index 83512b9dd22458c09f0da36f3686a73b489d6f8b..1e3fc22582408891d2af199b0a713c353af6ea43 100644 --- a/parsers/src/receipts.rs +++ b/parsers/src/receipts.rs @@ -4,14 +4,14 @@ // 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::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; /// Requests that this message is acked by the final recipient once /// received. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::RECEIPTS, name = "request")] pub struct Request; @@ -19,7 +19,7 @@ impl MessagePayload for Request {} /// Notes that a previous message has correctly been received, it is /// referenced by its 'id' attribute. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::RECEIPTS, name = "received")] pub struct Received { /// The 'id' attribute of the received message. diff --git a/parsers/src/rtt.rs b/parsers/src/rtt.rs index 91b5f08a3d1fd6a5405618639f576787a9ab0b1f..a80329123ed101f7cbb70d9116f5e24c8dcd9c66 100644 --- a/parsers/src/rtt.rs +++ b/parsers/src/rtt.rs @@ -4,7 +4,7 @@ // 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::{text::EmptyAsNone, FromXml, IntoXml}; +use xso::{text::EmptyAsNone, AsXml, FromXml}; use crate::ns; use crate::Element; @@ -31,7 +31,7 @@ generate_attribute!( ); /// Supports the transmission of text, including key presses, and text block inserts. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::RTT, name = "t")] pub struct Insert { /// Position in the message to start inserting from. If None, this means to start from the @@ -113,7 +113,7 @@ impl TryFrom for Erase { /// Allow for the transmission of intervals, between real-time text actions, to recreate the /// pauses between key presses. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::RTT, name = "w")] pub struct Wait { /// Amount of milliseconds to wait before the next action. diff --git a/parsers/src/sasl.rs b/parsers/src/sasl.rs index f27e3d8c30534e7254d42ec2cd371055730b2a85..59f20b5e9a9a0ab9c0f01f01d7cd62a734e94638 100644 --- a/parsers/src/sasl.rs +++ b/parsers/src/sasl.rs @@ -7,7 +7,7 @@ use xso::{ error::{Error, FromElementError}, text::Base64, - FromXml, IntoXml, + AsXml, FromXml, }; use crate::ns; @@ -48,7 +48,7 @@ generate_attribute!( /// The first step of the SASL process, selecting the mechanism and sending /// the first part of the handshake. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL, name = "auth")] pub struct Auth { /// The mechanism used. @@ -63,7 +63,7 @@ pub struct Auth { /// In case the mechanism selected at the [auth](struct.Auth.html) step /// requires a second step, the server sends this element with additional /// data. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL, name = "challenge")] pub struct Challenge { /// The challenge data. @@ -74,7 +74,7 @@ pub struct Challenge { /// In case the mechanism selected at the [auth](struct.Auth.html) step /// requires a second step, this contains the client’s response to the /// server’s [challenge](struct.Challenge.html). -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL, name = "response")] pub struct Response { /// The response data. @@ -84,12 +84,12 @@ pub struct Response { /// Sent by the client at any point after [auth](struct.Auth.html) if it /// wants to cancel the current authentication process. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL, name = "abort")] pub struct Abort; /// Sent by the server on SASL success. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SASL, name = "success")] pub struct Success { /// Possible data sent on success. diff --git a/parsers/src/sm.rs b/parsers/src/sm.rs index d7b3f25f2de217096dc91a3696b22f9d2adaee0f..c62bb66a4ccab432e29abec76f76f00980efc35c 100644 --- a/parsers/src/sm.rs +++ b/parsers/src/sm.rs @@ -4,13 +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 xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::ns; use crate::stanza_error::DefinedCondition; /// Acknowledgement of the currently received stanzas. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "a")] pub struct A { /// The last handled stanza. @@ -105,12 +105,12 @@ generate_element!( ); /// Requests the currently received stanzas by the other party. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "r")] pub struct R; /// Requests a stream resumption. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "resume")] pub struct Resume { /// The last handled stanza. @@ -124,7 +124,7 @@ pub struct Resume { } /// The response by the server for a successfully resumed stream. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "resumed")] pub struct Resumed { /// The last handled stanza. @@ -139,7 +139,7 @@ pub struct Resumed { // TODO: add support for optional and required. /// Represents availability of Stream Management in ``. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SM, name = "sm")] pub struct StreamManagement; diff --git a/parsers/src/stanza_id.rs b/parsers/src/stanza_id.rs index 3f304132695081f7684ea42afa9fba0cbad28f82..f9f346227a651b735b995826dea192256ff63c9b 100644 --- a/parsers/src/stanza_id.rs +++ b/parsers/src/stanza_id.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::message::MessagePayload; use crate::ns; @@ -12,7 +12,7 @@ use jid::Jid; /// 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)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SID, name = "stanza-id")] pub struct StanzaId { /// The id associated to this stanza by another entity. @@ -28,7 +28,7 @@ impl MessagePayload for StanzaId {} /// A hack for MUC before version 1.31 to track a message which may have /// its 'id' attribute changed. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::SID, name = "origin-id")] pub struct OriginId { /// The id this client set for this stanza. diff --git a/parsers/src/stream.rs b/parsers/src/stream.rs index 0b790305b4a181f2e20335e6d4e68a084f27ced6..594ce4d6144f5f104560d9ec28770bcd15272b6b 100644 --- a/parsers/src/stream.rs +++ b/parsers/src/stream.rs @@ -4,14 +4,14 @@ // 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::{AsXml, FromXml}; use jid::BareJid; use crate::ns; /// The stream opening for client-server communications. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::STREAM, name = "stream")] pub struct Stream { /// The JID of the entity opening this stream. diff --git a/parsers/src/time.rs b/parsers/src/time.rs index 17041a4a8cffe99376c125c3cd5e351754a2947c..86a31949a70f35365a5ac73e7850eb5d3f1a2a03 100644 --- a/parsers/src/time.rs +++ b/parsers/src/time.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::date::DateTime; use crate::iq::{IqGetPayload, IqResultPayload}; @@ -15,7 +15,7 @@ use std::str::FromStr; use xso::error::{Error, FromElementError}; /// An entity time query. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::TIME, name = "time")] pub struct TimeQuery; diff --git a/parsers/src/util/macro_tests.rs b/parsers/src/util/macro_tests.rs index c7b5f9e1abec97379f817a126a08e6d0d62cdc96..8cd42a2d47a3ccf3091ad61b1f97bb8dac6e2ecb 100644 --- a/parsers/src/util/macro_tests.rs +++ b/parsers/src/util/macro_tests.rs @@ -19,9 +19,9 @@ mod helpers { // this is to ensure that the macros do not have hidden dependencies on // any specific names being imported. use minidom::Element; - use xso::{error::FromElementError, transform, try_from_element, FromXml, IntoXml}; + use xso::{error::FromElementError, transform, try_from_element, AsXml, FromXml}; - pub(super) fn roundtrip_full( + pub(super) fn roundtrip_full( s: &str, ) { let initial: Element = s.parse().unwrap(); @@ -47,7 +47,7 @@ mod helpers { use self::helpers::{parse_str, roundtrip_full}; -use xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; // these are adverserial local names in order to trigger any issues with // unqualified names in the macro expansions. @@ -81,7 +81,7 @@ static BAR_NAME: &::xso::exports::rxml::strings::NcNameStr = { } }; -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "foo")] struct Empty; @@ -164,7 +164,7 @@ fn empty_qname_check_has_precedence_over_attr_check() { } } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = BAR_NAME)] struct NamePath; @@ -178,7 +178,7 @@ fn name_path_roundtrip() { roundtrip_full::(""); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = "urn:example:ns2", name = "baz")] struct NamespaceLit; @@ -192,7 +192,7 @@ fn namespace_lit_roundtrip() { roundtrip_full::(""); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct RequiredAttribute { #[xml(attribute)] @@ -237,7 +237,7 @@ fn required_attribute_missing() { } } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct RenamedAttribute { #[xml(attribute = "a1")] @@ -256,7 +256,7 @@ fn renamed_attribute_roundtrip() { roundtrip_full::(""); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct NamespacedAttribute { #[xml(attribute(namespace = "urn:example:ns1", name = FOO_NAME))] @@ -297,7 +297,7 @@ fn namespaced_attribute_roundtrip_b() { ); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct PrefixedAttribute { #[xml(attribute = "xml:lang")] @@ -314,7 +314,7 @@ fn prefixed_attribute_roundtrip() { roundtrip_full::(""); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct RequiredNonStringAttribute { #[xml(attribute)] @@ -331,7 +331,7 @@ fn required_non_string_attribute_roundtrip() { roundtrip_full::(""); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "attr")] struct DefaultAttribute { #[xml(attribute(default))] @@ -381,7 +381,7 @@ fn default_attribute_roundtrip_pp() { roundtrip_full::(""); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "text")] struct TextString { #[xml(text)] @@ -409,7 +409,7 @@ fn text_string_positive_preserves_whitespace() { assert_eq!(el.text, " \t\n"); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "text")] struct TextNonString { #[xml(text)] @@ -426,7 +426,7 @@ fn text_non_string_roundtrip() { roundtrip_full::("123456"); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem")] struct IgnoresWhitespaceWithoutTextConsumer; @@ -443,7 +443,7 @@ fn ignores_whitespace_without_text_consumer_positive() { .unwrap(); } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "elem")] struct FailsTextWithoutTextConsumer; @@ -465,7 +465,7 @@ fn fails_text_without_text_consumer_positive() { } } -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = NS1, name = "text")] struct TextWithCodec { #[xml(text(codec = xso::text::EmptyAsNone))] diff --git a/parsers/src/util/macros.rs b/parsers/src/util/macros.rs index 8f33acbb3bc8697e7fdc33994e7f5a4c233e7cdd..54503e2777409f87c5f20d9dd67318761999a861 100644 --- a/parsers/src/util/macros.rs +++ b/parsers/src/util/macros.rs @@ -94,9 +94,13 @@ macro_rules! generate_attribute { }) } } - impl ::xso::IntoXmlText for $elem { - fn into_xml_text(self) -> Result { - Ok(self.to_string()) + impl ::xso::AsXmlText for $elem { + fn as_xml_text(&self) -> Result<::std::borrow::Cow<'_, str>, xso::error::Error> { + match self { + $( + $elem::$a => Ok(::std::borrow::Cow::Borrowed($b)) + ),+ + } } } impl ::minidom::IntoAttributeValue for $elem { @@ -130,16 +134,16 @@ macro_rules! generate_attribute { s.parse().map_err(xso::error::Error::text_parse_error) } } - impl ::xso::IntoXmlText for $elem { - fn into_xml_text(self) -> Result { - Ok(String::from(match self { + impl ::xso::AsXmlText for $elem { + fn as_xml_text(&self) -> Result, xso::error::Error> { + Ok(std::borrow::Cow::Borrowed(match self { $($elem::$a => $b),+ })) } #[allow(unreachable_patterns)] - fn into_optional_xml_text(self) -> Result, xso::error::Error> { - Ok(Some(String::from(match self { + fn as_optional_xml_text(&self) -> Result>, xso::error::Error> { + Ok(Some(std::borrow::Cow::Borrowed(match self { $elem::$default => return Ok(None), $($elem::$a => $b),+ }))) @@ -219,17 +223,17 @@ macro_rules! generate_attribute { } } } - impl ::xso::IntoXmlText for $elem { - fn into_xml_text(self) -> Result { + impl ::xso::AsXmlText for $elem { + fn as_xml_text(&self) -> Result<::std::borrow::Cow<'_, str>, xso::error::Error> { match self { - Self::True => Ok("true".to_owned()), - Self::False => Ok("false".to_owned()), + Self::True => Ok(::std::borrow::Cow::Borrowed("true")), + Self::False => Ok(::std::borrow::Cow::Borrowed("false")), } } - fn into_optional_xml_text(self) -> Result, xso::error::Error> { + fn as_optional_xml_text(&self) -> Result>, xso::error::Error> { match self { - Self::True => Ok(Some("true".to_owned())), + Self::True => Ok(Some(::std::borrow::Cow::Borrowed("true"))), Self::False => Ok(None), } } @@ -435,9 +439,9 @@ macro_rules! generate_id { Ok(Self(s)) } } - impl ::xso::IntoXmlText for $elem { - fn into_xml_text(self) ->Result { - Ok(self.0) + impl ::xso::AsXmlText for $elem { + fn as_xml_text(&self) ->Result<::std::borrow::Cow<'_, str>, xso::error::Error> { + Ok(::std::borrow::Cow::Borrowed(self.0.as_str())) } } impl ::minidom::IntoAttributeValue for $elem { @@ -803,11 +807,11 @@ macro_rules! generate_element { } } - impl ::xso::IntoXml for $elem { - type EventIter = ::xso::minidom_compat::IntoEventsViaElement; + impl ::xso::AsXml for $elem { + type ItemIter<'x> = ::xso::minidom_compat::AsItemsViaElement<'x>; - fn into_event_iter(self) -> Result { - Self::EventIter::new(self) + fn as_xml_iter(&self) -> Result, ::xso::error::Error> { + ::xso::minidom_compat::AsItemsViaElement::new(self.clone()) } } ); diff --git a/parsers/src/vcard.rs b/parsers/src/vcard.rs index 867f1a28f73081c1dc82124e86fe9e500426e04f..2a2ea6db5aabef07a0c157e6efc9d9cb5e51171d 100644 --- a/parsers/src/vcard.rs +++ b/parsers/src/vcard.rs @@ -13,7 +13,7 @@ //! For vCard updates defined in [XEP-0153](https://xmpp.org/extensions/xep-0153.html), //! see [`vcard_update`][crate::vcard_update] module. -use xso::{FromXml, IntoXml}; +use xso::{AsXml, FromXml}; use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload}; use crate::util::text_node_codecs::{Codec, WhitespaceAwareBase64}; @@ -33,7 +33,7 @@ generate_element!( ); /// The type of the photo. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::VCARD, name = "TYPE")] pub struct Type { /// The type as a plain text string; at least "image/jpeg", "image/gif" and "image/png" SHOULD be supported. diff --git a/parsers/src/version.rs b/parsers/src/version.rs index 98ff9b4487f16bd3556b4cce1f1d634d479176ec..cf51f5fcba27ebc7514fdddd78c8ddea67ec5969 100644 --- a/parsers/src/version.rs +++ b/parsers/src/version.rs @@ -4,7 +4,7 @@ // 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::{AsXml, FromXml}; use crate::iq::{IqGetPayload, IqResultPayload}; use crate::ns; @@ -13,7 +13,7 @@ use crate::ns; /// /// It should only be used in an ``, as it can only /// represent the request, and not a result. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::VERSION, name = "query")] pub struct VersionQuery; diff --git a/parsers/src/websocket.rs b/parsers/src/websocket.rs index b2b84e1d256751fc3fcb15a98554517271c8de76..ebf829f73af8e7750d8e9b608fa0f6f0e45d0044 100644 --- a/parsers/src/websocket.rs +++ b/parsers/src/websocket.rs @@ -4,14 +4,14 @@ // 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::{AsXml, FromXml}; use jid::BareJid; use crate::ns; /// The stream opening for WebSocket. -#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)] +#[derive(FromXml, AsXml, PartialEq, Debug, Clone)] #[xml(namespace = ns::WEBSOCKET, name = "open")] pub struct Open { /// The JID of the entity opening this stream. diff --git a/xso-proc/Cargo.toml b/xso-proc/Cargo.toml index 5f8ef5d05444db0031e7722f935646684f8611d3..8d9a6fa432f2eaeced052ec81a96886fe2d2c220 100644 --- a/xso-proc/Cargo.toml +++ b/xso-proc/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.2" authors = [ "Jonas Schäfer ", ] -description = "Macro implementation of #[derive(FromXml, IntoXml)]" +description = "Macro implementation of #[derive(FromXml, AsXml)]" homepage = "https://xmpp.rs" repository = "https://gitlab.com/xmpp-rs/xmpp-rs" keywords = ["xso", "derive", "serialization"] diff --git a/xso-proc/src/compound.rs b/xso-proc/src/compound.rs index 41c12fe9f816ee20c9b9f472e8f193ebd5b3c0af..cfec95a1e96872018f584eb8b4b4004d0421eebc 100644 --- a/xso-proc/src/compound.rs +++ b/xso-proc/src/compound.rs @@ -12,9 +12,9 @@ use syn::{spanned::Spanned, *}; use crate::error_message::ParentRef; use crate::field::{FieldBuilderPart, FieldDef, FieldIteratorPart, FieldTempInit}; -use crate::scope::{mangle_member, FromEventsScope, IntoEventsScope}; -use crate::state::{FromEventsSubmachine, IntoEventsSubmachine, State}; -use crate::types::qname_ty; +use crate::scope::{mangle_member, AsItemsScope, FromEventsScope}; +use crate::state::{AsItemsSubmachine, FromEventsSubmachine, State}; +use crate::types::{namespace_ty, ncnamestr_cow_ty, phantom_lifetime_ty}; /// A struct or enum variant's contents. pub(crate) struct Compound { @@ -230,58 +230,86 @@ impl Compound { /// **Important:** The returned submachine is not in functional state! /// It's `init` must be modified so that a variable called `name` of type /// `rxml::QName` is in scope. - pub(crate) fn make_into_event_iter_statemachine( + pub(crate) fn make_as_item_iter_statemachine( &self, input_name: &Path, state_prefix: &str, - ) -> Result { - let scope = IntoEventsScope::new(); - let IntoEventsScope { ref attrs, .. } = scope; - - let start_element_state_ident = quote::format_ident!("{}StartElement", state_prefix); - let end_element_state_ident = quote::format_ident!("{}EndElement", state_prefix); + lifetime: &Lifetime, + ) -> Result { + let scope = AsItemsScope::new(lifetime); + + let element_head_start_state_ident = + quote::format_ident!("{}ElementHeadStart", state_prefix); + let element_head_end_state_ident = quote::format_ident!("{}ElementHeadEnd", state_prefix); + let element_foot_state_ident = quote::format_ident!("{}ElementFoot", state_prefix); let name_ident = quote::format_ident!("name"); + let ns_ident = quote::format_ident!("ns"); + let dummy_ident = quote::format_ident!("dummy"); let mut states = Vec::new(); - let mut init_body = TokenStream::default(); let mut destructure = TokenStream::default(); let mut start_init = TokenStream::default(); states.push( - State::new(start_element_state_ident.clone()) - .with_field(&name_ident, &qname_ty(Span::call_site())), + State::new(element_head_start_state_ident.clone()) + .with_field(&dummy_ident, &phantom_lifetime_ty(lifetime.clone())) + .with_field(&ns_ident, &namespace_ty(Span::call_site())) + .with_field( + &name_ident, + &ncnamestr_cow_ty(Span::call_site(), lifetime.clone()), + ), + ); + + let mut element_head_end_idx = states.len(); + states.push( + State::new(element_head_end_state_ident.clone()).with_impl(quote! { + ::core::option::Option::Some(::xso::Item::ElementHeadEnd) + }), ); for (i, field) in self.fields.iter().enumerate() { let member = field.member(); let bound_name = mangle_member(member); - let part = field.make_iterator_part(&scope, &bound_name)?; + let part = field.make_iterator_part(&bound_name)?; let state_name = quote::format_ident!("{}Field{}", state_prefix, i); + let ty = scope.borrow(field.ty().clone()); match part { - FieldIteratorPart::Header { setter } => { + FieldIteratorPart::Header { generator } => { + // we have to make sure that we carry our data around in + // all the previous states. + for state in &mut states[..element_head_end_idx] { + state.add_field(&bound_name, &ty); + } + states.insert( + element_head_end_idx, + State::new(state_name) + .with_field(&bound_name, &ty) + .with_impl(quote! { + #generator + }), + ); + element_head_end_idx += 1; + destructure.extend(quote! { - #member: #bound_name, + #member: ref #bound_name, }); - init_body.extend(setter); start_init.extend(quote! { #bound_name, }); - states[0].add_field(&bound_name, field.ty()); } FieldIteratorPart::Text { generator } => { // we have to make sure that we carry our data around in // all the previous states. for state in states.iter_mut() { - state.add_field(&bound_name, field.ty()); + state.add_field(&bound_name, &ty); } states.push( State::new(state_name) - .with_field(&bound_name, field.ty()) + .with_field(&bound_name, &ty) .with_impl(quote! { - #generator.map(|value| ::xso::exports::rxml::Event::Text( - ::xso::exports::rxml::parser::EventMetrics::zero(), + #generator.map(|value| ::xso::Item::Text( value, )) }), @@ -298,32 +326,27 @@ impl Compound { states[0].set_impl(quote! { { - let mut #attrs = ::xso::exports::rxml::AttrMap::new(); - #init_body - ::core::option::Option::Some(::xso::exports::rxml::Event::StartElement( - ::xso::exports::rxml::parser::EventMetrics::zero(), + ::core::option::Option::Some(::xso::Item::ElementHeadStart( + #ns_ident, #name_ident, - #attrs, )) } }); states.push( - State::new(end_element_state_ident.clone()).with_impl(quote! { - ::core::option::Option::Some(::xso::exports::rxml::Event::EndElement( - ::xso::exports::rxml::parser::EventMetrics::zero(), - )) + State::new(element_foot_state_ident.clone()).with_impl(quote! { + ::core::option::Option::Some(::xso::Item::ElementFoot) }), ); - Ok(IntoEventsSubmachine { + Ok(AsItemsSubmachine { defs: TokenStream::default(), states, destructure: quote! { #input_name { #destructure } }, init: quote! { - Self::#start_element_state_ident { #name_ident, #start_init } + Self::#element_head_start_state_ident { #dummy_ident: ::std::marker::PhantomData, #name_ident: name.1, #ns_ident: name.0, #start_init } }, }) } diff --git a/xso-proc/src/field.rs b/xso-proc/src/field.rs index 04a0358699b9bdaa15ce0c92e46e3aa6522a1a3c..de7c48c76d53766102c2f886ad65fd58504ace23 100644 --- a/xso-proc/src/field.rs +++ b/xso-proc/src/field.rs @@ -14,9 +14,9 @@ use rxml_validation::NcName; use crate::error_message::{self, ParentRef}; use crate::meta::{Flag, NameRef, NamespaceRef, XmlFieldMeta}; -use crate::scope::{FromEventsScope, IntoEventsScope}; +use crate::scope::FromEventsScope; use crate::types::{ - default_fn, from_xml_text_fn, into_optional_xml_text_fn, into_xml_text_fn, string_ty, + as_optional_xml_text_fn, as_xml_text_fn, default_fn, from_xml_text_fn, string_ty, text_codec_decode_fn, text_codec_encode_fn, }; @@ -69,13 +69,12 @@ pub(crate) enum FieldBuilderPart { pub(crate) enum FieldIteratorPart { /// The field is emitted as part of StartElement. Header { - /// A sequence of statements which updates the temporary variables - /// during the StartElement event's construction, consuming the - /// field's value. - setter: TokenStream, + /// An expression which consumes the field's value and returns a + /// `Item`. + generator: TokenStream, }, - /// The field is emitted as text event. + /// The field is emitted as text item. Text { /// An expression which consumes the field's value and returns a /// String, which is then emitted as text data. @@ -295,19 +294,13 @@ impl FieldDef { /// /// `bound_name` must be the name to which the field's value is bound in /// the iterator code. - pub(crate) fn make_iterator_part( - &self, - scope: &IntoEventsScope, - bound_name: &Ident, - ) -> Result { + pub(crate) fn make_iterator_part(&self, bound_name: &Ident) -> Result { match self.kind { FieldKind::Attribute { ref xml_name, ref xml_namespace, .. } => { - let IntoEventsScope { ref attrs, .. } = scope; - let xml_namespace = match xml_namespace { Some(v) => quote! { ::xso::exports::rxml::Namespace::from(#v) }, None => quote! { @@ -315,16 +308,13 @@ impl FieldDef { }, }; - let into_optional_xml_text = into_optional_xml_text_fn(self.ty.clone()); + let as_optional_xml_text = as_optional_xml_text_fn(self.ty.clone()); Ok(FieldIteratorPart::Header { - // This is a neat little trick: - // Option::from(x) converts x to an Option *unless* it - // already is an Option<_>. - setter: quote! { - #into_optional_xml_text(#bound_name)?.and_then(|#bound_name| #attrs.insert( + generator: quote! { + #as_optional_xml_text(#bound_name)?.map(|#bound_name| ::xso::Item::Attribute( #xml_namespace, - #xml_name.to_owned(), + ::std::borrow::Cow::Borrowed(#xml_name), #bound_name, )); }, @@ -338,8 +328,8 @@ impl FieldDef { quote! { #encode(#bound_name)? } } None => { - let into_xml_text = into_xml_text_fn(self.ty.clone()); - quote! { ::core::option::Option::Some(#into_xml_text(#bound_name)?) } + let as_xml_text = as_xml_text_fn(self.ty.clone()); + quote! { ::core::option::Option::Some(#as_xml_text(#bound_name)?) } } }; diff --git a/xso-proc/src/lib.rs b/xso-proc/src/lib.rs index c5b605975a542da310e17741138780a3c6113538..0e357a43cbd064409470c5782e49aa5f109de45c 100644 --- a/xso-proc/src/lib.rs +++ b/xso-proc/src/lib.rs @@ -111,26 +111,27 @@ pub fn from_xml(input: RawTokenStream) -> RawTokenStream { } } -/// Generate a `xso::IntoXml` implementation for the given item, or fail with +/// Generate a `xso::AsXml` implementation for the given item, or fail with /// a proper compiler error. -fn into_xml_impl(input: Item) -> Result { +fn as_xml_impl(input: Item) -> Result { let (vis, ident, def) = parse_struct(input)?; - let structs::IntoXmlParts { + let structs::AsXmlParts { defs, - into_event_iter_body, - event_iter_ty_ident, - } = def.make_into_event_iter(&vis)?; + as_xml_iter_body, + item_iter_ty_lifetime, + item_iter_ty, + } = def.make_as_xml_iter(&vis)?; #[cfg_attr(not(feature = "minidom"), allow(unused_mut))] let mut result = quote! { #defs - impl ::xso::IntoXml for #ident { - type EventIter = #event_iter_ty_ident; + impl ::xso::AsXml for #ident { + type ItemIter<#item_iter_ty_lifetime> = #item_iter_ty; - fn into_event_iter(self) -> ::core::result::Result { - #into_event_iter_body + fn as_xml_iter(&self) -> ::core::result::Result, ::xso::error::Error> { + #as_xml_iter_body } } }; @@ -162,15 +163,15 @@ fn into_xml_impl(input: Item) -> Result { Ok(result) } -/// Macro to derive a `xso::IntoXml` implementation on a type. +/// Macro to derive a `xso::AsXml` implementation on a type. /// /// The user-facing documentation for this macro lives in the `xso` crate. -#[proc_macro_derive(IntoXml, attributes(xml))] -pub fn into_xml(input: RawTokenStream) -> RawTokenStream { - // Shim wrapper around `into_xml_impl` which converts any errors into +#[proc_macro_derive(AsXml, attributes(xml))] +pub fn as_xml(input: RawTokenStream) -> RawTokenStream { + // Shim wrapper around `as_xml_impl` which converts any errors into // actual compiler errors within the resulting token stream. let item = syn::parse_macro_input!(input as Item); - match into_xml_impl(item) { + match as_xml_impl(item) { Ok(v) => v.into(), Err(e) => e.into_compile_error().into(), } diff --git a/xso-proc/src/scope.rs b/xso-proc/src/scope.rs index 1a4fbdc93349752fb96681bd365e0432d846eb65..bfe4c59439569e4fba54a112b44aef374bad86f6 100644 --- a/xso-proc/src/scope.rs +++ b/xso-proc/src/scope.rs @@ -9,6 +9,8 @@ use proc_macro2::Span; use syn::*; +use crate::types::ref_ty; + /// Container struct for various identifiers used throughout the parser code. /// /// This struct is passed around from the [`crate::compound::Compound`] @@ -80,19 +82,24 @@ impl FromEventsScope { /// same page about which identifiers are used for what. /// /// See [`FromEventsScope`] for recommendations on the usage. -pub(crate) struct IntoEventsScope { - /// Accesses the `AttrMap` from code in - /// [`crate::field::FieldIteratorPart::Header`]. - pub(crate) attrs: Ident, +pub(crate) struct AsItemsScope { + /// Lifetime for data borrowed by the implementation. + pub(crate) lifetime: Lifetime, } -impl IntoEventsScope { +impl AsItemsScope { /// Create a fresh scope with all necessary identifiers. - pub(crate) fn new() -> Self { + pub(crate) fn new(lifetime: &Lifetime) -> Self { Self { - attrs: Ident::new("attrs", Span::call_site()), + lifetime: lifetime.clone(), } } + + /// Create a reference to `ty`, borrowed for the lifetime of the AsXml + /// impl. + pub(crate) fn borrow(&self, ty: Type) -> Type { + ref_ty(ty, self.lifetime.clone()) + } } pub(crate) fn mangle_member(member: &Member) -> Ident { diff --git a/xso-proc/src/state.rs b/xso-proc/src/state.rs index 0e35d5fe1097cf3e86b94abfbfbec52d6453ee0e..b7899a0730172e9be9ce8903b3ac4a7e5402d508 100644 --- a/xso-proc/src/state.rs +++ b/xso-proc/src/state.rs @@ -82,7 +82,7 @@ impl State { /// the `advance` implementation of the state machine. /// /// See [`FromEventsStateMachine::advance_match_arms`] and - /// [`IntoEventsSubmachine::compile`] for the respective + /// [`AsItemsSubmachine::compile`] for the respective /// requirements on the implementations. pub(crate) fn with_impl(mut self, body: TokenStream) -> Self { self.advance_body = body; @@ -171,12 +171,12 @@ impl FromEventsSubmachine { } } -/// A partial [`IntoEventsStateMachine`] which only covers the builder for a +/// A partial [`AsItemsStateMachine`] which only covers the builder for a /// single compound. /// -/// See [`IntoEventsStateMachine`] for more information on the state machines +/// See [`AsItemsStateMachine`] for more information on the state machines /// in general. -pub(crate) struct IntoEventsSubmachine { +pub(crate) struct AsItemsSubmachine { /// Additional items necessary for the statemachine. pub(crate) defs: TokenStream, @@ -194,7 +194,7 @@ pub(crate) struct IntoEventsSubmachine { pub(crate) init: TokenStream, } -impl IntoEventsSubmachine { +impl AsItemsSubmachine { /// Convert a partial state machine into a full state machine. /// /// This converts the abstract [`State`] items into token @@ -202,13 +202,13 @@ impl IntoEventsSubmachine { /// definitions and the match arms), rendering them effectively immutable. /// /// This requires that the [`State::advance_body`] token streams evaluate - /// to an `Option`. If it evaluates to `Some(.)`, that is + /// to an `Option`. If it evaluates to `Some(.)`, that is /// emitted from the iterator. If it evaluates to `None`, the `advance` /// implementation is called again. /// /// Each state implementation is augmented to also enter the next state, /// causing the iterator to terminate eventually. - pub(crate) fn compile(self) -> IntoEventsStateMachine { + pub(crate) fn compile(self) -> AsItemsStateMachine { let mut state_defs = TokenStream::default(); let mut advance_match_arms = TokenStream::default(); @@ -227,13 +227,13 @@ impl IntoEventsSubmachine { .. }) => { quote! { - ::core::result::Result::Ok((::core::option::Option::Some(Self::#next_name { #construct_next }), event)) + ::core::result::Result::Ok((::core::option::Option::Some(Self::#next_name { #construct_next }), item)) } } // final state -> exit the state machine None => { quote! { - ::core::result::Result::Ok((::core::option::Option::None, event)) + ::core::result::Result::Ok((::core::option::Option::None, item)) } } }; @@ -244,17 +244,17 @@ impl IntoEventsSubmachine { advance_match_arms.extend(quote! { Self::#name { #destructure } => { - let event = #advance_body; + let item = #advance_body; #footer } }); } - IntoEventsStateMachine { + AsItemsStateMachine { defs: self.defs, state_defs, advance_match_arms, - variants: vec![IntoEventsEntryPoint { + variants: vec![AsItemsEntryPoint { init: self.init, destructure: self.destructure, }], @@ -281,7 +281,7 @@ pub(crate) struct FromEventsEntryPoint { } /// A single variant's entrypoint into the event iterator. -pub(crate) struct IntoEventsEntryPoint { +pub(crate) struct AsItemsEntryPoint { /// A pattern match which destructures the target type into its parts, for /// use by `init`. destructure: TokenStream, @@ -468,7 +468,7 @@ impl FromEventsStateMachine { /// method. That method consumes the enum value and returns either a new enum /// value, an error, or the output type of the state machine. #[derive(Default)] -pub(crate) struct IntoEventsStateMachine { +pub(crate) struct AsItemsStateMachine { /// Extra items which are needed for the state machine implementation. defs: TokenStream, @@ -480,7 +480,7 @@ pub(crate) struct IntoEventsStateMachine { /// enumeration type. /// /// Each match arm must either diverge or evaluate to a - /// `Result<(Option, Option), xso::error::Error>`, where + /// `Result<(Option, Option), xso::error::Error>`, where /// where `State` is the state enumeration. /// /// If `Some(.)` is returned for the event, that event is emitted. If @@ -489,7 +489,7 @@ pub(crate) struct IntoEventsStateMachine { /// field. /// /// If `None` is returned for the `Option`, the iterator - /// terminates yielding the `Option` value directly (even if it is + /// terminates yielding the `Option` value directly (even if it is /// `None`). After the iterator has terminated, it yields `None` /// indefinitely. advance_match_arms: TokenStream, @@ -498,10 +498,10 @@ pub(crate) struct IntoEventsStateMachine { /// /// This may only contain more than one element if an enumeration is being /// serialised by the resulting state machine. - variants: Vec, + variants: Vec, } -impl IntoEventsStateMachine { +impl AsItemsStateMachine { /// Render the state machine as a token stream. /// /// The token stream contains the following pieces: @@ -515,7 +515,8 @@ impl IntoEventsStateMachine { vis: &Visibility, input_ty: &Type, state_ty_ident: &Ident, - event_iter_ty_ident: &Ident, + item_iter_ty_lifetime: &Lifetime, + item_iter_ty: &Type, ) -> Result { let Self { defs, @@ -525,10 +526,10 @@ impl IntoEventsStateMachine { } = self; let input_ty_ref = make_ty_ref(input_ty); - let docstr = format!("Convert a {0} into XML events.\n\nThis type is generated using the [`macro@xso::IntoXml`] derive macro and implements [`std::iter:Iterator`] for {0}.", input_ty_ref); + let docstr = format!("Convert a {0} into XML events.\n\nThis type is generated using the [`macro@xso::AsXml`] derive macro and implements [`std::iter:Iterator`] for {0}.", input_ty_ref); let init_body = if variants.len() == 1 { - let IntoEventsEntryPoint { destructure, init } = variants.remove(0); + let AsItemsEntryPoint { destructure, init } = variants.remove(0); quote! { { let #destructure = value; @@ -537,7 +538,7 @@ impl IntoEventsStateMachine { } } else { let mut match_arms = TokenStream::default(); - for IntoEventsEntryPoint { destructure, init } in variants { + for AsItemsEntryPoint { destructure, init } in variants { match_arms.extend(quote! { #destructure => #init, }); @@ -553,40 +554,40 @@ impl IntoEventsStateMachine { Ok(quote! { #defs - enum #state_ty_ident { + enum #state_ty_ident<#item_iter_ty_lifetime> { #state_defs } - impl #state_ty_ident { - fn advance(mut self) -> ::core::result::Result<(::core::option::Option, ::core::option::Option<::xso::exports::rxml::Event>), ::xso::error::Error> { + impl<#item_iter_ty_lifetime> #state_ty_ident<#item_iter_ty_lifetime> { + fn advance(mut self) -> ::core::result::Result<(::core::option::Option, ::core::option::Option<::xso::Item<#item_iter_ty_lifetime>>), ::xso::error::Error> { match self { #advance_match_arms } } fn new( - value: #input_ty, + value: &#item_iter_ty_lifetime #input_ty, ) -> ::core::result::Result { ::core::result::Result::Ok(#init_body) } } #[doc = #docstr] - #vis struct #event_iter_ty_ident(::core::option::Option<#state_ty_ident>); + #vis struct #item_iter_ty(::core::option::Option<#state_ty_ident<#item_iter_ty_lifetime>>); - impl ::std::iter::Iterator for #event_iter_ty_ident { - type Item = ::core::result::Result<::xso::exports::rxml::Event, ::xso::error::Error>; + impl<#item_iter_ty_lifetime> ::std::iter::Iterator for #item_iter_ty { + type Item = ::core::result::Result<::xso::Item<#item_iter_ty_lifetime>, ::xso::error::Error>; fn next(&mut self) -> ::core::option::Option { let mut state = self.0.take()?; loop { - let (next_state, ev) = match state.advance() { + let (next_state, item) = match state.advance() { ::core::result::Result::Ok(v) => v, ::core::result::Result::Err(e) => return ::core::option::Option::Some(::core::result::Result::Err(e)), }; - if let ::core::option::Option::Some(ev) = ev { + if let ::core::option::Option::Some(item) = item { self.0 = next_state; - return ::core::option::Option::Some(::core::result::Result::Ok(ev)); + return ::core::option::Option::Some(::core::result::Result::Ok(item)); } // no event, do we have a state? if let ::core::option::Option::Some(st) = next_state { @@ -602,8 +603,8 @@ impl IntoEventsStateMachine { } } - impl #event_iter_ty_ident { - fn new(value: #input_ty) -> ::core::result::Result { + impl<#item_iter_ty_lifetime> #item_iter_ty { + fn new(value: &#item_iter_ty_lifetime #input_ty) -> ::core::result::Result { #state_ty_ident::new(value).map(|ok| Self(::core::option::Option::Some(ok))) } } diff --git a/xso-proc/src/structs.rs b/xso-proc/src/structs.rs index 5242cefb8a9d6b3ecc9a1a0389a6a6dbb7e2fd8f..9106ffeeb62815e1cff1bae2caae4a3b777c213b 100644 --- a/xso-proc/src/structs.rs +++ b/xso-proc/src/structs.rs @@ -6,7 +6,7 @@ //! Handling of structs -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::*; @@ -25,16 +25,19 @@ pub(crate) struct FromXmlParts { pub(crate) builder_ty_ident: Ident, } -/// Parts necessary to construct a `::xso::IntoXml` implementation. -pub(crate) struct IntoXmlParts { +/// Parts necessary to construct a `::xso::AsXml` implementation. +pub(crate) struct AsXmlParts { /// Additional items necessary for the implementation. pub(crate) defs: TokenStream, - /// The body of the `::xso::IntoXml::into_event_iter` function. - pub(crate) into_event_iter_body: TokenStream, + /// The body of the `::xso::AsXml::as_xml_iter` function. + pub(crate) as_xml_iter_body: TokenStream, - /// The name of the type which is the `::xso::IntoXml::EventIter`. - pub(crate) event_iter_ty_ident: Ident, + /// The type which is the `::xso::AsXml::ItemIter`. + pub(crate) item_iter_ty: Type, + + /// The lifetime name used in `item_iter_ty`. + pub(crate) item_iter_ty_lifetime: Lifetime, } /// Definition of a struct and how to parse it. @@ -55,7 +58,7 @@ pub(crate) struct StructDef { builder_ty_ident: Ident, /// Name of the iterator type. - event_iter_ty_ident: Ident, + item_iter_ty_ident: Ident, /// Flag whether debug mode is enabled. debug: bool, @@ -78,7 +81,7 @@ impl StructDef { inner: Compound::from_fields(fields)?, target_ty_ident: ident.clone(), builder_ty_ident: quote::format_ident!("{}FromXmlBuilder", ident), - event_iter_ty_ident: quote::format_ident!("{}IntoXmlIterator", ident), + item_iter_ty_ident: quote::format_ident!("{}AsXmlIterator", ident), debug: meta.debug.is_set(), }) } @@ -136,22 +139,53 @@ impl StructDef { }) } - pub(crate) fn make_into_event_iter(&self, vis: &Visibility) -> Result { + pub(crate) fn make_as_xml_iter(&self, vis: &Visibility) -> Result { let xml_namespace = &self.namespace; let xml_name = &self.name; let target_ty_ident = &self.target_ty_ident; - let event_iter_ty_ident = &self.event_iter_ty_ident; - let state_ty_ident = quote::format_ident!("{}State", event_iter_ty_ident); + let item_iter_ty_ident = &self.item_iter_ty_ident; + let item_iter_ty_lifetime = Lifetime { + apostrophe: Span::call_site(), + ident: Ident::new("xso_proc_as_xml_iter_lifetime", Span::call_site()), + }; + let item_iter_ty = Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: None, + segments: [PathSegment { + ident: item_iter_ty_ident.clone(), + arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token: None, + lt_token: token::Lt { + spans: [Span::call_site()], + }, + args: [GenericArgument::Lifetime(item_iter_ty_lifetime.clone())] + .into_iter() + .collect(), + gt_token: token::Gt { + spans: [Span::call_site()], + }, + }), + }] + .into_iter() + .collect(), + }, + }); + let state_ty_ident = quote::format_ident!("{}State", item_iter_ty_ident); let defs = self .inner - .make_into_event_iter_statemachine(&target_ty_ident.clone().into(), "Struct")? + .make_as_item_iter_statemachine( + &target_ty_ident.clone().into(), + "Struct", + &item_iter_ty_lifetime, + )? .with_augmented_init(|init| { quote! { let name = ( ::xso::exports::rxml::Namespace::from(#xml_namespace), - #xml_name.into(), + ::std::borrow::Cow::Borrowed(#xml_name), ); #init } @@ -165,15 +199,17 @@ impl StructDef { } .into(), &state_ty_ident, - event_iter_ty_ident, + &item_iter_ty_lifetime, + &item_iter_ty, )?; - Ok(IntoXmlParts { + Ok(AsXmlParts { defs, - into_event_iter_body: quote! { - #event_iter_ty_ident::new(self) + as_xml_iter_body: quote! { + #item_iter_ty_ident::new(self) }, - event_iter_ty_ident: event_iter_ty_ident.clone(), + item_iter_ty, + item_iter_ty_lifetime, }) } diff --git a/xso-proc/src/types.rs b/xso-proc/src/types.rs index db438024cc80a83398e0f9ba42b557ae6fcdcd4e..70b9f93b79afc54616da9a16997b38703649417e 100644 --- a/xso-proc/src/types.rs +++ b/xso-proc/src/types.rs @@ -9,8 +9,8 @@ use proc_macro2::Span; use syn::{spanned::Spanned, *}; -/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::QName`. -pub(crate) fn qname_ty(span: Span) -> Type { +/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::Namespace`. +pub(crate) fn namespace_ty(span: Span) -> Type { Type::Path(TypePath { qself: None, path: Path { @@ -31,7 +31,7 @@ pub(crate) fn qname_ty(span: Span) -> Type { arguments: PathArguments::None, }, PathSegment { - ident: Ident::new("QName", span), + ident: Ident::new("Namespace", span), arguments: PathArguments::None, }, ] @@ -41,6 +41,83 @@ pub(crate) fn qname_ty(span: Span) -> Type { }) } +/// Construct a [`syn::Type`] referring to `::xso::exports::rxml::NcNameStr`. +pub(crate) fn ncnamestr_ty(span: Span) -> Type { + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: Some(syn::token::PathSep { + spans: [span, span], + }), + segments: [ + PathSegment { + ident: Ident::new("xso", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("exports", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("rxml", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("NcNameStr", span), + arguments: PathArguments::None, + }, + ] + .into_iter() + .collect(), + }, + }) +} + +/// Construct a [`syn::Type`] referring to `Cow<#lifetime, #ty>`. +pub(crate) fn cow_ty(ty: Type, lifetime: Lifetime) -> Type { + let span = ty.span(); + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: Some(syn::token::PathSep { + spans: [span, span], + }), + segments: [ + PathSegment { + ident: Ident::new("std", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("borrow", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("Cow", span), + arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token: None, + lt_token: token::Lt { spans: [span] }, + args: [ + GenericArgument::Lifetime(lifetime), + GenericArgument::Type(ty), + ] + .into_iter() + .collect(), + gt_token: token::Gt { spans: [span] }, + }), + }, + ] + .into_iter() + .collect(), + }, + }) +} + +/// Construct a [`syn::Type`] referring to +/// `Cow<#lifetime, ::rxml::NcNameStr>`. +pub(crate) fn ncnamestr_cow_ty(ty_span: Span, lifetime: Lifetime) -> Type { + cow_ty(ncnamestr_ty(ty_span), lifetime) +} + /// Construct a [`syn::Expr`] referring to /// `<#ty as ::xso::FromXmlText>::from_xml_text`. pub(crate) fn from_xml_text_fn(ty: Type) -> Expr { @@ -79,8 +156,8 @@ pub(crate) fn from_xml_text_fn(ty: Type) -> Expr { } /// Construct a [`syn::Expr`] referring to -/// `<#ty as ::xso::IntoOptionalXmlText>::into_optional_xml_text`. -pub(crate) fn into_optional_xml_text_fn(ty: Type) -> Expr { +/// `<#ty as ::xso::AsOptionalXmlText>::as_optional_xml_text`. +pub(crate) fn as_optional_xml_text_fn(ty: Type) -> Expr { let span = ty.span(); Expr::Path(ExprPath { attrs: Vec::new(), @@ -101,11 +178,11 @@ pub(crate) fn into_optional_xml_text_fn(ty: Type) -> Expr { arguments: PathArguments::None, }, PathSegment { - ident: Ident::new("IntoOptionalXmlText", span), + ident: Ident::new("AsOptionalXmlText", span), arguments: PathArguments::None, }, PathSegment { - ident: Ident::new("into_optional_xml_text", span), + ident: Ident::new("as_optional_xml_text", span), arguments: PathArguments::None, }, ] @@ -185,8 +262,8 @@ pub(crate) fn string_ty(span: Span) -> Type { } /// Construct a [`syn::Expr`] referring to -/// `<#ty as ::xso::IntoXmlText>::into_xml_text`. -pub(crate) fn into_xml_text_fn(ty: Type) -> Expr { +/// `<#ty as ::xso::AsXmlText>::as_xml_text`. +pub(crate) fn as_xml_text_fn(ty: Type) -> Expr { let span = ty.span(); Expr::Path(ExprPath { attrs: Vec::new(), @@ -207,11 +284,11 @@ pub(crate) fn into_xml_text_fn(ty: Type) -> Expr { arguments: PathArguments::None, }, PathSegment { - ident: Ident::new("IntoXmlText", span), + ident: Ident::new("AsXmlText", span), arguments: PathArguments::None, }, PathSegment { - ident: Ident::new("into_xml_text", span), + ident: Ident::new("as_xml_text", span), arguments: PathArguments::None, }, ] @@ -293,3 +370,55 @@ pub(crate) fn text_codec_decode_fn(codec_ty: Type, for_ty: Type) -> Expr { path: ty.path, }) } + +/// Construct a [`syn::Type`] for `&#lifetime #ty`. +pub(crate) fn ref_ty(ty: Type, lifetime: Lifetime) -> Type { + let span = ty.span(); + Type::Reference(TypeReference { + and_token: token::And { spans: [span] }, + lifetime: Some(lifetime), + mutability: None, + elem: Box::new(ty), + }) +} + +/// Construct a [`syn::Type`] referring to +/// `::std::marker::PhantomData<&#lifetime ()>`. +pub(crate) fn phantom_lifetime_ty(lifetime: Lifetime) -> Type { + let span = lifetime.span(); + let dummy = Type::Tuple(TypeTuple { + paren_token: token::Paren::default(), + elems: punctuated::Punctuated::default(), + }); + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: Some(syn::token::PathSep { + spans: [span, span], + }), + segments: [ + PathSegment { + ident: Ident::new("std", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("marker", span), + arguments: PathArguments::None, + }, + PathSegment { + ident: Ident::new("PhantomData", span), + arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token: None, + lt_token: token::Lt { spans: [span] }, + args: [GenericArgument::Type(ref_ty(dummy, lifetime))] + .into_iter() + .collect(), + gt_token: token::Gt { spans: [span] }, + }), + }, + ] + .into_iter() + .collect(), + }, + }) +} diff --git a/xso/src/from_xml_doc.md b/xso/src/from_xml_doc.md index 90073ba6ca793a4e2a1dab985f217a7049393ed1..7ff83bd0bcf83287d2375d99874fb8112059a561 100644 --- a/xso/src/from_xml_doc.md +++ b/xso/src/from_xml_doc.md @@ -1,7 +1,7 @@ # Make a struct or enum parseable from XML This derives the [`FromXml`] trait on a struct or enum. It is the counterpart -to [`macro@IntoXml`]. +to [`macro@AsXml`]. ## Example @@ -75,7 +75,7 @@ The following mapping types are defined: The `attribute` meta causes the field to be mapped to an XML attribute of the same name. For `FromXml`, the field's type must implement [`FromXmlText`] and -for `IntoXml`, the field's type must implement [`IntoOptionalXmlText`]. +for `AsXml`, the field's type must implement [`AsOptionalXmlText`]. The following keys can be used inside the `#[xml(attribute(..))]` meta: @@ -99,7 +99,7 @@ otherwise). If `default` is specified and the attribute is absent in the source, the value is generated using [`std::default::Default`], requiring the field type to implement the `Default` trait for a `FromXml` derivation. `default` has no -influence on `IntoXml`. +influence on `AsXml`. ##### Example @@ -148,14 +148,14 @@ If `codec` is given, the given `codec` must implement [`TextCodec`][`TextCodec`] where `T` is the type of the field. If `codec` is *not* given, the field's type must implement [`FromXmlText`] for -`FromXml` and for `IntoXml`, the field's type must implement [`IntoXmlText`]. +`FromXml` and for `AsXml`, the field's type must implement [`AsXmlText`]. The `text` meta also supports a shorthand syntax, `#[xml(text = ..)]`, where the value is treated as the value for the `codec` key (with optional prefix as described above, and unnamespaced otherwise). Only a single field per struct may be annotated with `#[xml(text)]` at a time, -to avoid parsing ambiguities. This is also true if only `IntoXml` is derived on +to avoid parsing ambiguities. This is also true if only `AsXml` is derived on a field, for consistency. ##### Example without codec diff --git a/xso/src/lib.rs b/xso/src/lib.rs index 6a66d1c353209fc1c6cc0f1f4d6b838878022cc6..9d5407480fba3452343033ddec396adc214d4bf8 100644 --- a/xso/src/lib.rs +++ b/xso/src/lib.rs @@ -49,14 +49,14 @@ pub use xso_proc::FromXml; /// # Make a struct or enum serialisable to XML /// -/// This derives the [`IntoXml`] trait on a struct or enum. It is the +/// This derives the [`AsXml`] trait on a struct or enum. It is the /// counterpart to [`macro@FromXml`]. /// /// The attributes necessary and available for the derivation to work are /// documented on [`macro@FromXml`]. #[doc(inline)] #[cfg(feature = "macros")] -pub use xso_proc::IntoXml; +pub use xso_proc::AsXml; /// Trait allowing to consume a struct and iterate its contents as /// serialisable [`rxml::Event`] items. @@ -369,10 +369,10 @@ impl AsOptionalXmlText for Option { } } -/// Attempt to transform a type implementing [`IntoXml`] into another +/// Attempt to transform a type implementing [`AsXml`] into another /// type which implements [`FromXml`]. -pub fn transform(from: F) -> Result { - let mut iter = from.into_event_iter()?; +pub fn transform(from: F) -> Result { + let mut iter = self::rxml_util::ItemToEvent::new(from.as_xml_iter()?); let (qname, attrs) = match iter.next() { Some(Ok(rxml::Event::StartElement(_, qname, attrs))) => (qname, attrs), Some(Err(e)) => return Err(e), @@ -418,8 +418,24 @@ pub fn try_from_element( } }; - let mut iter = from.into_event_iter()?; - iter.next().expect("first event from minidom::Element")?; + let mut iter = from.as_xml_iter()?; + // consume the element header + for item in &mut iter { + let item = item?; + match item { + // discard the element header + Item::XmlDeclaration(..) => (), + Item::ElementHeadStart(..) => (), + Item::Attribute(..) => (), + Item::ElementHeadEnd => { + // now that the element header is over, we break out + break; + } + Item::Text(..) => panic!("text before end of element header"), + Item::ElementFoot => panic!("element foot before end of element header"), + } + } + let iter = self::rxml_util::ItemToEvent::new(iter); for event in iter { let event = event?; if let Some(v) = sink.feed(event)? { diff --git a/xso/src/rxml_util.rs b/xso/src/rxml_util.rs index 36f9564691823127e4edf98e58883a7b1ff5b3a3..7f5fcd60fb753f8652cd1b4f11bbac7603c6bdb5 100644 --- a/xso/src/rxml_util.rs +++ b/xso/src/rxml_util.rs @@ -8,9 +8,7 @@ use std::borrow::Cow; -use rxml::{Namespace, NcNameStr, XmlVersion}; -#[cfg(feature = "minidom")] -use rxml::Event; +use rxml::{parser::EventMetrics, AttrMap, Event, Namespace, NcName, NcNameStr, XmlVersion}; /// An encodable item. /// @@ -164,12 +162,130 @@ impl>> Iterator for EventT } } +/// Iterator adapter which converts an iterator over [`Item`] to +/// an iterator over [`Event`][`crate::Event`]. +/// +/// As `Event` does not support borrowing data, this iterator copies the data +/// from the items on the fly. +pub(crate) struct ItemToEvent { + inner: I, + event_buffer: Option, + elem_buffer: Option<(Namespace, NcName, AttrMap)>, +} + +impl<'x, I: Iterator, crate::error::Error>>> ItemToEvent { + /// Create a new adapter with `inner` as the source iterator. + pub(crate) fn new(inner: I) -> Self { + Self { + inner, + event_buffer: None, + elem_buffer: None, + } + } +} + +impl ItemToEvent { + fn update<'x>(&mut self, item: Item<'x>) -> Result, crate::error::Error> { + assert!(self.event_buffer.is_none()); + match item { + Item::XmlDeclaration(v) => { + assert!(self.elem_buffer.is_none()); + Ok(Some(Event::XmlDeclaration(EventMetrics::zero(), v))) + } + Item::ElementHeadStart(ns, name) => { + if self.elem_buffer.is_some() { + // this is only used with AsXml implementations, so + // triggering this is always a coding failure instead of a + // runtime error. + panic!("got a second ElementHeadStart items without ElementHeadEnd inbetween: ns={:?} name={:?} (state={:?})", ns, name, self.elem_buffer); + } + self.elem_buffer = Some((ns.to_owned(), name.into_owned(), AttrMap::new())); + Ok(None) + } + Item::Attribute(ns, name, value) => { + let Some((_, _, attrs)) = self.elem_buffer.as_mut() else { + // this is only used with AsXml implementations, so + // triggering this is always a coding failure instead of a + // runtime error. + panic!( + "got a second Attribute item without ElementHeadStart: ns={:?}, name={:?}", + ns, name + ); + }; + attrs.insert(ns, name.into_owned(), value.into_owned()); + Ok(None) + } + Item::ElementHeadEnd => { + let Some((ns, name, attrs)) = self.elem_buffer.take() else { + // this is only used with AsXml implementations, so + // triggering this is always a coding failure instead of a + // runtime error. + panic!( + "got ElementHeadEnd item without ElementHeadStart: {:?}", + item + ); + }; + Ok(Some(Event::StartElement( + EventMetrics::zero(), + (ns, name), + attrs, + ))) + } + Item::Text(value) => { + if let Some(elem_buffer) = self.elem_buffer.as_ref() { + // this is only used with AsXml implementations, so + // triggering this is always a coding failure instead of a + // runtime error. + panic!("got Text after ElementHeadStart but before ElementHeadEnd: Text({:?}) (state = {:?})", value, elem_buffer); + } + Ok(Some(Event::Text(EventMetrics::zero(), value.into_owned()))) + } + Item::ElementFoot => { + let end_ev = Event::EndElement(EventMetrics::zero()); + let result = if let Some((ns, name, attrs)) = self.elem_buffer.take() { + // content-less element + self.event_buffer = Some(end_ev); + Event::StartElement(EventMetrics::zero(), (ns, name), attrs) + } else { + end_ev + }; + Ok(Some(result)) + } + } + } +} + +impl<'x, I: Iterator, crate::error::Error>>> Iterator for ItemToEvent { + type Item = Result; + + fn next(&mut self) -> Option { + if let Some(event) = self.event_buffer.take() { + return Some(Ok(event)); + } + loop { + let item = match self.inner.next() { + Some(Ok(v)) => v, + Some(Err(e)) => return Some(Err(e)), + None => return None, + }; + match self.update(item).transpose() { + Some(v) => return Some(v), + None => (), + } + } + } + + fn size_hint(&self) -> (usize, Option) { + // we may create an indefinte amount of items for a single event, + // so we cannot provide a reasonable upper bound. + (self.inner.size_hint().0, None) + } +} + #[cfg(all(test, feature = "minidom"))] mod tests_minidom { use std::convert::TryInto; - use rxml::{parser::EventMetrics, AttrMap}; - use super::*; fn events_to_items>(events: I) -> Vec> { @@ -307,3 +423,144 @@ mod tests_minidom { }; } } + +#[cfg(test)] +mod tests { + use std::convert::TryInto; + + use super::*; + + fn items_to_events<'x, I: IntoIterator>>( + items: I, + ) -> Result, crate::error::Error> { + let iter = ItemToEvent { + inner: items.into_iter().map(|x| Ok(x)), + event_buffer: None, + elem_buffer: None, + }; + let mut result = Vec::new(); + for ev in iter { + let ev = ev?; + result.push(ev); + } + Ok(result) + } + + #[test] + fn item_to_event_xml_decl() { + let items = vec![Item::XmlDeclaration(XmlVersion::V1_0)]; + let events = items_to_events(items).expect("item conversion"); + assert_eq!(events.len(), 1); + match events[0] { + Event::XmlDeclaration(_, XmlVersion::V1_0) => (), + ref other => panic!("unexected event in position 0: {:?}", other), + }; + } + + #[test] + fn item_to_event_simple_empty_element() { + let items = vec![ + Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())), + Item::ElementHeadEnd, + Item::ElementFoot, + ]; + let events = items_to_events(items).expect("item conversion"); + assert_eq!(events.len(), 2); + match events[0] { + Event::StartElement(_, (ref ns, ref name), ref attrs) => { + assert_eq!(attrs.len(), 0); + assert_eq!(ns, Namespace::none()); + assert_eq!(name, "elem"); + } + ref other => panic!("unexected event in position 0: {:?}", other), + }; + match events[1] { + Event::EndElement(_) => (), + ref other => panic!("unexected event in position 1: {:?}", other), + }; + } + + #[test] + fn item_to_event_short_empty_element() { + let items = vec![ + Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())), + Item::ElementFoot, + ]; + let events = items_to_events(items).expect("item conversion"); + assert_eq!(events.len(), 2); + match events[0] { + Event::StartElement(_, (ref ns, ref name), ref attrs) => { + assert_eq!(attrs.len(), 0); + assert_eq!(ns, Namespace::none()); + assert_eq!(name, "elem"); + } + ref other => panic!("unexected event in position 0: {:?}", other), + }; + match events[1] { + Event::EndElement(_) => (), + ref other => panic!("unexected event in position 1: {:?}", other), + }; + } + + #[test] + fn item_to_event_element_with_text_content() { + let items = vec![ + Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())), + Item::ElementHeadEnd, + Item::Text(Cow::Borrowed("Hello World!")), + Item::ElementFoot, + ]; + let events = items_to_events(items).expect("item conversion"); + assert_eq!(events.len(), 3); + match events[0] { + Event::StartElement(_, (ref ns, ref name), ref attrs) => { + assert_eq!(attrs.len(), 0); + assert_eq!(ns, Namespace::none()); + assert_eq!(name, "elem"); + } + ref other => panic!("unexected event in position 0: {:?}", other), + }; + match events[1] { + Event::Text(_, ref value) => { + assert_eq!(value, "Hello World!"); + } + ref other => panic!("unexected event in position 1: {:?}", other), + }; + match events[2] { + Event::EndElement(_) => (), + ref other => panic!("unexected event in position 2: {:?}", other), + }; + } + + #[test] + fn item_to_event_element_with_attributes() { + let items = vec![ + Item::ElementHeadStart(Namespace::NONE, Cow::Borrowed("elem".try_into().unwrap())), + Item::Attribute( + Namespace::NONE, + Cow::Borrowed("attr".try_into().unwrap()), + Cow::Borrowed("value"), + ), + Item::ElementHeadEnd, + Item::ElementFoot, + ]; + let events = items_to_events(items).expect("item conversion"); + assert_eq!(events.len(), 2); + match events[0] { + Event::StartElement(_, (ref ns, ref name), ref attrs) => { + assert_eq!(ns, Namespace::none()); + assert_eq!(name, "elem"); + assert_eq!(attrs.len(), 1); + assert_eq!( + attrs.get(Namespace::none(), "attr").map(|x| x.as_str()), + Some("value") + ); + } + ref other => panic!("unexected event in position 0: {:?}", other), + }; + match events[1] { + Event::EndElement(_) => (), + ref other => panic!("unexected event in position 2: {:?}", other), + }; + } +} diff --git a/xso/src/text.rs b/xso/src/text.rs index 40664e2c1ff87f1aa49fa8524aaafab2689299c3..767062e543357a804fe3e50890b4c18f8b6ae299 100644 --- a/xso/src/text.rs +++ b/xso/src/text.rs @@ -134,7 +134,7 @@ convert_via_fromstr_and_display! { /// Represent a way to encode/decode text data into a Rust type. /// /// This trait can be used in scenarios where implementing [`FromXmlText`] -/// and/or [`IntoXmlText`] on a type is not feasible or sensible, such as the +/// and/or [`AsXmlText`] on a type is not feasible or sensible, such as the /// following: /// /// 1. The type originates in a foreign crate, preventing the implementation @@ -143,7 +143,7 @@ convert_via_fromstr_and_display! { /// 2. There is more than one way to convert a value to/from XML. /// /// The codec to use for a text can be specified in the attributes understood -/// by `FromXml` and `IntoXml` derive macros. See the documentation of the +/// by `FromXml` and `AsXml` derive macros. See the documentation of the /// [`FromXml`][`macro@crate::FromXml`] derive macro for details. pub trait TextCodec { /// Decode a string value into the type. @@ -152,7 +152,7 @@ pub trait TextCodec { /// Encode the type as string value. /// /// If this returns `None`, the string value is not emitted at all. - fn encode(value: T) -> Result, Error>; + fn encode(value: &T) -> Result>, Error>; } /// Text codec which does no transform. @@ -163,8 +163,8 @@ impl TextCodec for Plain { Ok(s) } - fn encode(value: String) -> Result, Error> { - Ok(Some(value)) + fn encode(value: &String) -> Result>, Error> { + Ok(Some(Cow::Borrowed(value.as_str()))) } } @@ -180,9 +180,9 @@ impl TextCodec> for EmptyAsNone { } } - fn encode(value: Option) -> Result, Error> { - Ok(match value { - Some(v) if !v.is_empty() => Some(v), + fn encode(value: &Option) -> Result>, Error> { + Ok(match value.as_ref() { + Some(v) if !v.is_empty() => Some(Cow::Borrowed(v.as_str())), Some(_) | None => None, }) } @@ -237,8 +237,8 @@ impl TextCodec> for Base64 { .map_err(Error::text_parse_error) } - fn encode(value: Vec) -> Result, Error> { - Ok(Some(StandardBase64Engine.encode(&value))) + fn encode(value: &Vec) -> Result>, Error> { + Ok(Some(Cow::Owned(StandardBase64Engine.encode(&value)))) } } @@ -252,7 +252,11 @@ impl TextCodec>> for Base64 { Ok(Some(Self::decode(s)?)) } - fn encode(decoded: Option>) -> Result, Error> { - decoded.map(Self::encode).transpose().map(Option::flatten) + fn encode(decoded: &Option>) -> Result>, Error> { + decoded + .as_ref() + .map(Self::encode) + .transpose() + .map(Option::flatten) } }