xmpp-parsers: Convert http_upload::Header to xso

We can’t use generate_attribute!() for HeaderName because it has to be
case insensitive.
This commit is contained in:
Emmanuel Gil Peyrot 2025-01-27 03:19:57 +01:00
commit 543d9eb590
3 changed files with 55 additions and 68 deletions

View file

@ -48,6 +48,9 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
ssm::StreamManagement::optional and starttls::StartTls::required are ssm::StreamManagement::optional and starttls::StartTls::required are
now proper bools, instead of Option<ZeroSizedType> (!518) now proper bools, instead of Option<ZeroSizedType> (!518)
- vCard-temp queries have been split into vcard::VCardQuery (!522) - vCard-temp queries have been split into vcard::VCardQuery (!522)
- http_upload’s Header type has been split into HeaderName, an enum
containing only the name of the header, and the Header struct with
both a name and a value(!530)
* New parsers/serialisers: * New parsers/serialisers:
- Stream Features (RFC 6120) (!400) - Stream Features (RFC 6120) (!400)
- Spam Reporting (XEP-0377) (!506) - Spam Reporting (XEP-0377) (!506)

View file

@ -4,15 +4,11 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use xso::{ use xso::{error::Error, AsXml, AsXmlText, FromXml, FromXmlText};
error::{Error, FromElementError, FromEventsError},
exports::rxml,
minidom_compat, AsXml, FromXml,
};
use crate::iq::{IqGetPayload, IqResultPayload}; use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns; use crate::ns;
use minidom::Element; use alloc::borrow::Cow;
/// Requesting a slot /// Requesting a slot
#[derive(FromXml, AsXml, Debug, Clone, PartialEq)] #[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
@ -33,32 +29,37 @@ pub struct SlotRequest {
impl IqGetPayload for SlotRequest {} impl IqGetPayload for SlotRequest {}
/// Slot header /// All three possible header names.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Header { pub enum HeaderName {
/// Authorization header /// Authorization header
Authorization(String), Authorization,
/// Cookie header /// Cookie header
Cookie(String), Cookie,
/// Expires header /// Expires header
Expires(String), Expires,
} }
impl TryFrom<Element> for Header { impl HeaderName {
type Error = FromElementError; /// Returns the string version of this enum value.
fn try_from(elem: Element) -> Result<Header, FromElementError> { pub fn as_str(&self) -> &'static str {
check_self!(elem, "header", HTTP_UPLOAD); match self {
check_no_children!(elem, "header"); HeaderName::Authorization => "Authorization",
check_no_unknown_attributes!(elem, "header", ["name"]); HeaderName::Cookie => "Cookie",
let name: String = get_attr!(elem, "name", Required); HeaderName::Expires => "Expires",
let text = elem.text(); }
}
}
Ok(match name.to_lowercase().as_str() { impl FromXmlText for HeaderName {
"authorization" => Header::Authorization(text), fn from_xml_text(mut s: String) -> Result<Self, Error> {
"cookie" => Header::Cookie(text), s.make_ascii_lowercase();
"expires" => Header::Expires(text), Ok(match s.as_str() {
"authorization" => HeaderName::Authorization,
"cookie" => HeaderName::Cookie,
"expires" => HeaderName::Expires,
_ => { _ => {
return Err(Error::Other( return Err(Error::Other(
"Header name must be either 'Authorization', 'Cookie', or 'Expires'.", "Header name must be either 'Authorization', 'Cookie', or 'Expires'.",
@ -69,41 +70,23 @@ impl TryFrom<Element> for Header {
} }
} }
impl FromXml for Header { impl AsXmlText for HeaderName {
type Builder = minidom_compat::FromEventsViaElement<Header>; fn as_xml_text(&self) -> Result<Cow<'_, str>, Error> {
Ok(Cow::Borrowed(self.as_str()))
fn from_events(
qname: rxml::QName,
attrs: rxml::AttrMap,
) -> Result<Self::Builder, FromEventsError> {
if qname.0 != ns::HTTP_UPLOAD || qname.1 != "header" {
return Err(FromEventsError::Mismatch { name: qname, attrs });
}
Self::Builder::new(qname, attrs)
} }
} }
impl From<Header> for Element { /// Slot header
fn from(elem: Header) -> Element { #[derive(FromXml, AsXml, Debug, Clone, PartialEq)]
let (attr, val) = match elem { #[xml(namespace = ns::HTTP_UPLOAD, name = "header")]
Header::Authorization(val) => ("Authorization", val), pub struct Header {
Header::Cookie(val) => ("Cookie", val), /// Name of the header
Header::Expires(val) => ("Expires", val), #[xml(attribute)]
}; pub name: HeaderName,
Element::builder("header", ns::HTTP_UPLOAD) /// Value of the header
.attr("name", attr) #[xml(text)]
.append(val) pub value: String,
.build()
}
}
impl AsXml for Header {
type ItemIter<'x> = minidom_compat::AsItemsViaElement<'x>;
fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, Error> {
minidom_compat::AsItemsViaElement::new(self.clone())
}
} }
/// Put URL /// Put URL
@ -146,11 +129,13 @@ impl IqResultPayload for SlotResult {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use minidom::Element;
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
#[test] #[test]
fn test_size() { fn test_size() {
assert_size!(SlotRequest, 32); assert_size!(SlotRequest, 32);
assert_size!(HeaderName, 1);
assert_size!(Header, 16); assert_size!(Header, 16);
assert_size!(Put, 24); assert_size!(Put, 24);
assert_size!(Get, 12); assert_size!(Get, 12);
@ -161,6 +146,7 @@ mod tests {
#[test] #[test]
fn test_size() { fn test_size() {
assert_size!(SlotRequest, 56); assert_size!(SlotRequest, 56);
assert_size!(HeaderName, 1);
assert_size!(Header, 32); assert_size!(Header, 32);
assert_size!(Put, 48); assert_size!(Put, 48);
assert_size!(Get, 24); assert_size!(Get, 24);
@ -196,11 +182,17 @@ mod tests {
assert_eq!(slot.put.url, String::from("https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg")); assert_eq!(slot.put.url, String::from("https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg"));
assert_eq!( assert_eq!(
slot.put.headers[0], slot.put.headers[0],
Header::Authorization(String::from("Basic Base64String==")) Header {
name: HeaderName::Authorization,
value: String::from("Basic Base64String==")
}
); );
assert_eq!( assert_eq!(
slot.put.headers[1], slot.put.headers[1],
Header::Cookie(String::from("foo=bar; user=romeo")) Header {
name: HeaderName::Cookie,
value: String::from("foo=bar; user=romeo")
}
); );
assert_eq!(slot.get.url, String::from("https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg")); assert_eq!(slot.get.url, String::from("https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg"));
} }

View file

@ -10,11 +10,7 @@ use reqwest::{
use std::path::PathBuf; use std::path::PathBuf;
use tokio::fs::File; use tokio::fs::File;
use tokio_util::codec::{BytesCodec, FramedRead}; use tokio_util::codec::{BytesCodec, FramedRead};
use tokio_xmpp::{ use tokio_xmpp::{jid::Jid, minidom::Element, parsers::http_upload::SlotResult};
jid::Jid,
minidom::Element,
parsers::http_upload::{Header as HttpUploadHeader, SlotResult},
};
use crate::{Agent, Event}; use crate::{Agent, Event};
@ -39,12 +35,8 @@ pub async fn handle_upload_result(
let mut headers = ReqwestHeaderMap::new(); let mut headers = ReqwestHeaderMap::new();
for header in slot.put.headers { for header in slot.put.headers {
let (attr, val) = match header { let attr = header.name.as_str();
HttpUploadHeader::Authorization(val) => ("Authorization", val), headers.insert(attr, header.value.parse().unwrap());
HttpUploadHeader::Cookie(val) => ("Cookie", val),
HttpUploadHeader::Expires(val) => ("Expires", val),
};
headers.insert(attr, val.parse().unwrap());
} }
let web = ReqwestClient::new(); let web = ReqwestClient::new();