xml: Make serialization more ergonomic and clippy appeasement

This commit is contained in:
Lennart K
2025-06-29 17:00:10 +02:00
parent 891ef6a9f3
commit 828e7399c8
9 changed files with 72 additions and 87 deletions

View File

@@ -111,11 +111,10 @@ impl<T1: XmlSerialize, T2: XmlSerialize> axum::response::IntoResponse
fn into_response(self) -> axum::response::Response { fn into_response(self) -> axum::response::Response {
use axum::body::Body; use axum::body::Body;
let mut output: Vec<_> = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".into(); let output = match self.serialize_to_string() {
let mut writer = quick_xml::Writer::new_with_indent(&mut output, b' ', 4); Ok(out) => out,
if let Err(err) = self.serialize_root(&mut writer) { Err(err) => return crate::Error::from(err).into_response(),
return crate::Error::from(err).into_response(); };
}
let mut resp = axum::response::Response::builder().status(StatusCode::MULTI_STATUS); let mut resp = axum::response::Response::builder().status(StatusCode::MULTI_STATUS);
let hdrs = resp.headers_mut().unwrap(); let hdrs = resp.headers_mut().unwrap();

View File

@@ -23,20 +23,17 @@ mod tests {
#[test] #[test]
fn test_serialize_resourcetype() { fn test_serialize_resourcetype() {
let mut buf = Vec::new(); let out = Document {
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
resourcetype: Resourcetype(&[ resourcetype: Resourcetype(&[
ResourcetypeInner(Some(crate::namespace::NS_DAV), "displayname"), ResourcetypeInner(Some(crate::namespace::NS_DAV), "displayname"),
ResourcetypeInner(Some(crate::namespace::NS_CALENDARSERVER), "calendar-color"), ResourcetypeInner(Some(crate::namespace::NS_CALENDARSERVER), "calendar-color"),
]), ]),
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
assert_eq!( assert_eq!(
out, out,
"<document><resourcetype><displayname xmlns=\"DAV:\"/><calendar-color xmlns=\"http://calendarserver.org/ns/\"/></resourcetype></document>" "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<document><resourcetype><displayname xmlns=\"DAV:\"/><calendar-color xmlns=\"http://calendarserver.org/ns/\"/></resourcetype></document>"
) )
} }
} }

View File

@@ -99,13 +99,13 @@ impl<S: SubscriptionStore> DavPushController<S> {
content_update, content_update,
}; };
let mut output: Vec<_> = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".into(); let payload = match push_message.serialize_to_string() {
let mut writer = quick_xml::Writer::new_with_indent(&mut output, b' ', 4); Ok(payload) => payload,
if let Err(err) = push_message.serialize_root(&mut writer) { Err(err) => {
error!("Could not serialize push message: {}", err); error!("Could not serialize push message: {}", err);
return; return;
} }
let payload = String::from_utf8(output).unwrap(); };
for subsciption in subscriptions { for subsciption in subscriptions {
if let Some(allowed_push_servers) = &self.allowed_push_servers { if let Some(allowed_push_servers) = &self.allowed_push_servers {

View File

@@ -1,6 +1,6 @@
use crate::XmlRootTag; use crate::XmlRootTag;
use quick_xml::{ use quick_xml::{
events::{attributes::Attribute, BytesStart, Event}, events::{BytesStart, Event, attributes::Attribute},
name::{Namespace, QName}, name::{Namespace, QName},
}; };
use std::collections::HashMap; use std::collections::HashMap;
@@ -44,6 +44,13 @@ pub trait XmlSerializeRoot {
&self, &self,
writer: &mut quick_xml::Writer<W>, writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()>; ) -> std::io::Result<()>;
fn serialize_to_string(&self) -> std::io::Result<String> {
let mut buf: Vec<_> = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".into();
let mut writer = quick_xml::Writer::new(&mut buf);
self.serialize_root(&mut writer)?;
Ok(String::from_utf8_lossy(&buf).to_string())
}
} }
impl<T: XmlSerialize + XmlRootTag> XmlSerializeRoot for T { impl<T: XmlSerialize + XmlRootTag> XmlSerializeRoot for T {

View File

@@ -198,6 +198,7 @@ fn test_struct_generics() {
#[derive(XmlDeserialize, XmlRootTag)] #[derive(XmlDeserialize, XmlRootTag)]
#[xml(root = b"document")] #[xml(root = b"document")]
struct Document<T: XmlDeserialize> { struct Document<T: XmlDeserialize> {
#[allow(dead_code)]
child: T, child: T,
} }
@@ -218,6 +219,7 @@ fn test_struct_unparsed() {
#[derive(XmlDeserialize, XmlRootTag)] #[derive(XmlDeserialize, XmlRootTag)]
#[xml(root = b"document")] #[xml(root = b"document")]
struct Document { struct Document {
#[allow(dead_code)]
child: Unparsed, child: Unparsed,
} }

View File

@@ -23,12 +23,16 @@ enum ExtensionProp {
enum CalendarProp { enum CalendarProp {
// WebDAV (RFC 2518) // WebDAV (RFC 2518)
#[xml(ns = "NS_DAV")] #[xml(ns = "NS_DAV")]
#[allow(dead_code)]
Displayname(Option<String>), Displayname(Option<String>),
#[xml(ns = "NS_DAV")] #[xml(ns = "NS_DAV")]
#[allow(dead_code)]
Getcontenttype(&'static str), Getcontenttype(&'static str),
#[xml(ns = "NS_DAV", rename = b"principal-URL")] #[xml(ns = "NS_DAV", rename = b"principal-URL")]
#[allow(dead_code)]
PrincipalUrl, PrincipalUrl,
#[allow(dead_code)]
Topic, Topic,
} }

View File

@@ -4,29 +4,34 @@ use rustical_xml::{Unparsed, XmlDeserialize, XmlDocument, XmlRootTag};
fn test_propertyupdate() { fn test_propertyupdate() {
#[derive(XmlDeserialize)] #[derive(XmlDeserialize)]
struct SetPropertyElement<T: XmlDeserialize> { struct SetPropertyElement<T: XmlDeserialize> {
#[allow(dead_code)]
prop: T, prop: T,
} }
#[derive(XmlDeserialize)] #[derive(XmlDeserialize)]
struct TagName { struct TagName {
#[xml(ty = "tag_name")] #[xml(ty = "tag_name")]
#[allow(dead_code)]
name: String, name: String,
} }
#[derive(XmlDeserialize)] #[derive(XmlDeserialize)]
struct PropertyElement { struct PropertyElement {
#[xml(ty = "untagged")] #[xml(ty = "untagged")]
#[allow(dead_code)]
property: TagName, property: TagName,
} }
#[derive(XmlDeserialize)] #[derive(XmlDeserialize)]
struct RemovePropertyElement { struct RemovePropertyElement {
#[allow(dead_code)]
prop: PropertyElement, prop: PropertyElement,
} }
#[derive(XmlDeserialize)] #[derive(XmlDeserialize)]
enum Operation<T: XmlDeserialize> { enum Operation<T: XmlDeserialize> {
Set(SetPropertyElement<T>), Set(SetPropertyElement<T>),
#[allow(dead_code)]
Remove(RemovePropertyElement), Remove(RemovePropertyElement),
} }
@@ -34,10 +39,11 @@ fn test_propertyupdate() {
#[xml(root = b"propertyupdate")] #[xml(root = b"propertyupdate")]
struct PropertyupdateElement<T: XmlDeserialize> { struct PropertyupdateElement<T: XmlDeserialize> {
#[xml(ty = "untagged", flatten)] #[xml(ty = "untagged", flatten)]
#[allow(dead_code)]
operations: Vec<Operation<T>>, operations: Vec<Operation<T>>,
} }
let doc = PropertyupdateElement::<Unparsed>::parse_str( PropertyupdateElement::<Unparsed>::parse_str(
r#" r#"
<propertyupdate> <propertyupdate>
<set> <set>

View File

@@ -11,17 +11,17 @@ fn test_struct_value_tagged() {
#[derive(Debug, XmlSerialize, PartialEq)] #[derive(Debug, XmlSerialize, PartialEq)]
enum Prop { enum Prop {
Test(String), Test(String),
Hello(usize), // Hello(usize),
Unit, // Unit,
} }
let mut buf = Vec::new(); let out = Document {
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
prop: Prop::Test("asd".to_owned()), prop: Prop::Test("asd".to_owned()),
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap(); assert_eq!(
assert_eq!(out, "<propfind><prop><test>asd</test></prop></propfind>"); out,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<propfind><prop><test>asd</test></prop></propfind>"
);
} }

View File

@@ -1,11 +1,7 @@
use std::{
borrow::{Borrow, Cow},
collections::HashMap,
};
use quick_xml::Writer; use quick_xml::Writer;
use quick_xml::name::Namespace; use quick_xml::name::Namespace;
use rustical_xml::{XmlDocument, XmlRootTag, XmlSerialize, XmlSerializeRoot}; use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
use std::collections::HashMap;
use xml_derive::XmlDeserialize; use xml_derive::XmlDeserialize;
#[test] #[test]
@@ -22,16 +18,13 @@ fn test_struct_document() {
text: String, text: String,
} }
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document { Document {
child: Child { child: Child {
text: "asd".to_owned(), text: "asd".to_owned(),
}, },
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
} }
#[test] #[test]
@@ -51,17 +44,14 @@ fn test_struct_untagged_attr() {
text: String, text: String,
} }
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document { Document {
name: "okay".to_owned(), name: "okay".to_owned(),
child: Child { child: Child {
text: "asd".to_owned(), text: "asd".to_owned(),
}, },
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
} }
#[test] #[test]
@@ -73,16 +63,16 @@ fn test_struct_value_tagged() {
num: usize, num: usize,
} }
let mut buf = Vec::new(); let out = Document {
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
href: "okay".to_owned(), href: "okay".to_owned(),
num: 123, num: 123,
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap(); assert_eq!(
assert_eq!(out, "<document><href>okay</href><num>123</num></document>"); out,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<document><href>okay</href><num>123</num></document>"
);
} }
#[test] #[test]
@@ -94,15 +84,15 @@ fn test_struct_value_untagged() {
href: String, href: String,
} }
let mut buf = Vec::new(); let out = Document {
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
href: "okays".to_owned(), href: "okays".to_owned(),
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap(); assert_eq!(
assert_eq!(out, "<document>okays</document>"); out,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<document>okays</document>"
);
} }
#[test] #[test]
@@ -114,17 +104,14 @@ fn test_struct_vec() {
href: Vec<String>, href: Vec<String>,
} }
let mut buf = Vec::new(); let out = Document {
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
href: vec!["okay".to_owned(), "wow".to_owned()], href: vec!["okay".to_owned(), "wow".to_owned()],
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
assert_eq!( assert_eq!(
out, out,
"<document><href>okay</href><href>wow</href></document>" "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<document><href>okay</href><href>wow</href></document>"
); );
} }
@@ -147,15 +134,15 @@ fn test_struct_serialize_with() {
val.to_uppercase().serialize(ns, tag, namespaces, writer) val.to_uppercase().serialize(ns, tag, namespaces, writer)
} }
let mut buf = Vec::new(); let out = Document {
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
href: "okay".to_owned(), href: "okay".to_owned(),
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap(); assert_eq!(
assert_eq!(out, "<document><href>OKAY</href></document>"); out,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<document><href>OKAY</href></document>"
);
} }
#[test] #[test]
@@ -173,8 +160,6 @@ fn test_struct_tag_list() {
name: String, name: String,
} }
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document { Document {
tags: vec![ tags: vec![
Tag { Tag {
@@ -188,10 +173,8 @@ fn test_struct_tag_list() {
}, },
], ],
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
dbg!(out);
} }
#[test] #[test]
@@ -205,15 +188,11 @@ fn test_struct_ns() {
child: String, child: String,
} }
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document { Document {
child: "hello!".to_string(), child: "hello!".to_string(),
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
dbg!(out);
} }
#[test] #[test]
@@ -227,16 +206,11 @@ fn test_struct_tuple() {
#[derive(Debug, XmlSerialize, PartialEq, Default)] #[derive(Debug, XmlSerialize, PartialEq, Default)]
struct Child(#[xml(ty = "tag_name")] String, #[xml(ty = "text")] String); struct Child(#[xml(ty = "tag_name")] String, #[xml(ty = "text")] String);
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document { Document {
child: Child("child".to_owned(), "Hello!".to_owned()), child: Child("child".to_owned(), "Hello!".to_owned()),
} }
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
dbg!(out);
} }
#[test] #[test]
@@ -247,11 +221,7 @@ fn test_tuple_struct() {
#[xml(root = b"document")] #[xml(root = b"document")]
struct Document(#[xml(ns = "NS", rename = b"okay")] String); struct Document(#[xml(ns = "NS", rename = b"okay")] String);
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document("hello!".to_string()) Document("hello!".to_string())
.serialize_root(&mut writer) .serialize_to_string()
.unwrap(); .unwrap();
let out = String::from_utf8(buf).unwrap();
dbg!(out);
} }