From f77333e55dccf61d76b4f0025d7dfff7e5c70768 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:40:11 +0100 Subject: [PATCH] xml: Work on struct serialization --- crates/xml/derive/src/field.rs | 5 ++-- crates/xml/derive/src/xml_struct.rs | 38 ++++++++++++++++++++++++++--- crates/xml/src/de.rs | 7 +----- crates/xml/src/error.rs | 2 ++ crates/xml/src/lib.rs | 8 +++++- crates/xml/src/se.rs | 22 +++++++++++++++++ crates/xml/tests/se_struct.rs | 37 +++++++++++++++++++++++++--- 7 files changed, 102 insertions(+), 17 deletions(-) diff --git a/crates/xml/derive/src/field.rs b/crates/xml/derive/src/field.rs index c3bdf99..ed763cd 100644 --- a/crates/xml/derive/src/field.rs +++ b/crates/xml/derive/src/field.rs @@ -258,11 +258,10 @@ impl Field { writer.write_event(Event::Text(BytesText::new(&self.#field_ident)))?; }), FieldType::Tag => Some(quote! { - self.#field_ident.serialize(Some(#field_name), writer)?; + self.#field_ident.serialize(None, Some(#field_name), writer)?; }), FieldType::Untagged => Some(quote! { - // TODO: untag! - self.#field_ident.serialize(None, writer)?; + self.#field_ident.serialize(None, None, writer)?; }), // TODO: Think about what to do here FieldType::TagName | FieldType::Namespace => None, diff --git a/crates/xml/derive/src/xml_struct.rs b/crates/xml/derive/src/xml_struct.rs index 0e14889..ba714c4 100644 --- a/crates/xml/derive/src/xml_struct.rs +++ b/crates/xml/derive/src/xml_struct.rs @@ -1,5 +1,5 @@ -use crate::attrs::StructAttrs; -use crate::Field; +use crate::attrs::{FieldType, StructAttrs}; +use crate::{field, Field}; use core::panic; use darling::FromDeriveInput; use quote::quote; @@ -181,11 +181,34 @@ impl NamedStruct { let ident = &self.ident; let tag_writers = self.fields.iter().map(Field::tag_writer); - // TODO: Implement attributes + let untagged_attributes = self + .fields + .iter() + .filter(|field| field.attrs.xml_ty == FieldType::Untagged) + .map(|field| { + let field_ident = field.field_ident(); + quote! { bytes_start.extend_attributes(self.#field_ident.attributes()); } + }); + + let attributes = self + .fields + .iter() + .filter(|field| field.attrs.xml_ty == FieldType::Attr) + .map(|field| { + let field_name = field.xml_name(); + quote! { + ::quick_xml::events::attributes::Attribute { + key: ::quick_xml::name::QName(#field_name), + value: b"hello".into() + } + } + }); + quote! { impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause { fn serialize( &self, + ns: Option<&[u8]>, tag: Option<&[u8]>, writer: &mut ::quick_xml::Writer ) -> ::std::io::Result<()> { @@ -194,7 +217,10 @@ impl NamedStruct { let tag_str = tag.map(String::from_utf8_lossy); if let Some(tag) = &tag_str { - writer.write_event(Event::Start(BytesStart::new(tag.to_owned())))?; + let mut bytes_start = BytesStart::new(tag.to_owned()); + bytes_start.extend_attributes(self.attributes()); + #(#untagged_attributes);* + writer.write_event(Event::Start(bytes_start))?; } #(#tag_writers);* if let Some(tag) = &tag_str { @@ -202,6 +228,10 @@ impl NamedStruct { } Ok(()) } + + fn attributes<'a>(&self) -> Vec<::quick_xml::events::attributes::Attribute<'a>> { + vec![ #(#attributes),* ] + } } } } diff --git a/crates/xml/src/de.rs b/crates/xml/src/de.rs index cb13bc7..bfae62e 100644 --- a/crates/xml/src/de.rs +++ b/crates/xml/src/de.rs @@ -3,11 +3,11 @@ use quick_xml::name::ResolveResult; use std::io::BufRead; pub use xml_derive::XmlDeserialize; pub use xml_derive::XmlDocument; -pub use xml_derive::XmlRootTag; use quick_xml::events::{BytesStart, Event}; use crate::XmlDeError; +use crate::XmlRootTag; pub trait XmlDeserialize: Sized { fn deserialize( @@ -17,11 +17,6 @@ pub trait XmlDeserialize: Sized { ) -> Result; } -pub trait XmlRootTag { - fn root_tag() -> &'static [u8]; - fn root_ns() -> Option<&'static [u8]>; -} - pub trait XmlDocument: XmlDeserialize { fn parse(reader: quick_xml::NsReader) -> Result; diff --git a/crates/xml/src/error.rs b/crates/xml/src/error.rs index f558b6d..5ea9b76 100644 --- a/crates/xml/src/error.rs +++ b/crates/xml/src/error.rs @@ -16,6 +16,8 @@ pub enum XmlDeError { Eof, #[error("Unsupported xml event: {0}")] UnsupportedEvent(&'static str), + #[error("{0}")] + Other(String), #[error("Invalid variant: {0}")] InvalidVariant(String), #[error("Invalid field name in {0}: {1}")] diff --git a/crates/xml/src/lib.rs b/crates/xml/src/lib.rs index b3cb91b..2ea4e8f 100644 --- a/crates/xml/src/lib.rs +++ b/crates/xml/src/lib.rs @@ -8,10 +8,11 @@ mod value; pub use de::XmlDeserialize; pub use de::XmlDocument; -pub use de::XmlRootTag; pub use error::XmlDeError; pub use se::XmlSerialize; +pub use se::XmlSerializeRoot; pub use value::Value; +pub use xml_derive::XmlRootTag; impl XmlDeserialize for () { fn deserialize( @@ -58,3 +59,8 @@ impl XmlDeserialize for Unparsed { Ok(Self(start.to_owned())) } } + +pub trait XmlRootTag { + fn root_tag() -> &'static [u8]; + fn root_ns() -> Option<&'static [u8]>; +} diff --git a/crates/xml/src/se.rs b/crates/xml/src/se.rs index 7db6f9b..38549f2 100644 --- a/crates/xml/src/se.rs +++ b/crates/xml/src/se.rs @@ -1,9 +1,31 @@ +use quick_xml::events::attributes::Attribute; pub use xml_derive::XmlSerialize; +use crate::XmlRootTag; + pub trait XmlSerialize { fn serialize( &self, + ns: Option<&[u8]>, tag: Option<&[u8]>, writer: &mut quick_xml::Writer, ) -> std::io::Result<()>; + + fn attributes<'a>(&self) -> impl IntoIterator>>; +} + +pub trait XmlSerializeRoot { + fn serialize_root( + &self, + writer: &mut quick_xml::Writer, + ) -> std::io::Result<()>; +} + +impl XmlSerializeRoot for T { + fn serialize_root( + &self, + writer: &mut quick_xml::Writer, + ) -> std::io::Result<()> { + self.serialize(Self::root_ns(), Some(Self::root_tag()), writer) + } } diff --git a/crates/xml/tests/se_struct.rs b/crates/xml/tests/se_struct.rs index 3b33c1c..5e99fb3 100644 --- a/crates/xml/tests/se_struct.rs +++ b/crates/xml/tests/se_struct.rs @@ -1,9 +1,9 @@ -use rustical_xml::{XmlRootTag, XmlSerialize}; +use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; use xml_derive::XmlDeserialize; #[test] fn test_struct_document() { - #[derive(Debug, XmlRootTag, XmlSerialize, XmlDeserialize, PartialEq)] + #[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)] #[xml(root = b"document")] struct Document { child: Child, @@ -22,7 +22,38 @@ fn test_struct_document() { text: "asd".to_owned(), }, } - .serialize(Some(Document::root_tag()), &mut writer) + .serialize_root(&mut writer) + .unwrap(); + let out = String::from_utf8(buf).unwrap(); + dbg!(out); +} + +#[test] +fn test_struct_untagged_attr() { + #[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)] + #[xml(root = b"document")] + struct Document { + #[xml(ty = "attr")] + name: String, + #[xml(ty = "untagged")] + child: Child, + } + + #[derive(Debug, XmlSerialize, PartialEq, Default)] + struct Child { + #[xml(ty = "attr")] + text: String, + } + + let mut buf = Vec::new(); + let mut writer = quick_xml::Writer::new(&mut buf); + Document { + name: "okay".to_owned(), + child: Child { + text: "asd".to_owned(), + }, + } + .serialize_root(&mut writer) .unwrap(); let out = String::from_utf8(buf).unwrap(); dbg!(out);