xml: Add serialization

This commit is contained in:
Lennart
2024-12-23 10:46:33 +01:00
parent d9f7d1da2f
commit b5e0f68239
9 changed files with 141 additions and 33 deletions

View File

@@ -127,7 +127,7 @@ impl Enum {
quote! {
impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause {
fn deserialize<R: std::io::BufRead>(
fn deserialize<R: ::std::io::BufRead>(
reader: &mut quick_xml::NsReader<R>,
start: &quick_xml::events::BytesStart,
empty: bool
@@ -191,4 +191,32 @@ impl Enum {
generics: input.generics.to_owned(),
}
}
pub fn impl_se(&self) -> proc_macro2::TokenStream {
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
let ident = &self.ident;
// TODO: Implement attributes
quote! {
impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause {
fn serialize<W: ::std::io::Write>(
&self,
tag: Option<&[u8]>,
writer: &mut ::quick_xml::Writer<W>
) -> ::std::io::Result<()> {
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
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())))?;
}
if let Some(tag) = &tag_str {
writer.write_event(Event::End(BytesEnd::new(tag.to_owned())))?;
}
Ok(())
}
}
}
}
}

View File

@@ -82,7 +82,7 @@ impl NamedStruct {
quote! {
impl #impl_generics ::rustical_xml::XmlDeserialize for #ident #type_generics #where_clause {
fn deserialize<R: BufRead>(
fn deserialize<R: ::std::io::BufRead>(
reader: &mut quick_xml::NsReader<R>,
start: &quick_xml::events::BytesStart,
empty: bool
@@ -162,4 +162,34 @@ impl NamedStruct {
}
}
}
pub fn impl_se(&self) -> proc_macro2::TokenStream {
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
let ident = &self.ident;
let tag_writers = self.fields.iter().map(Field::tag_writer);
// TODO: Implement attributes
quote! {
impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause {
fn serialize<W: ::std::io::Write>(
&self,
tag: Option<&[u8]>,
writer: &mut ::quick_xml::Writer<W>
) -> ::std::io::Result<()> {
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
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())))?;
}
#(#tag_writers);*
if let Some(tag) = &tag_str {
writer.write_event(Event::End(BytesEnd::new(tag.to_owned())))?;
}
Ok(())
}
}
}
}
}

View File

@@ -218,4 +218,23 @@ impl Field {
}
})
}
pub fn tag_writer(&self) -> Option<proc_macro2::TokenStream> {
let field_ident = self.field_ident();
let field_name = self.xml_name();
match self.attrs.xml_ty {
FieldType::Attr => None,
FieldType::Text => Some(quote! {
writer.write_event(Event::Text(BytesText::new(&self.#field_ident)))?;
}),
FieldType::Tag => Some(quote! {
self.#field_ident.serialize(Some(#field_name), writer)?;
}),
FieldType::Untagged => Some(quote! {
// TODO: untag!
self.#field_ident.serialize(None, writer)?;
}),
}
}
}

View File

@@ -16,6 +16,18 @@ pub fn derive_xml_deserialize(input: proc_macro::TokenStream) -> proc_macro::Tok
.into()
}
#[proc_macro_derive(XmlSerialize, attributes(xml))]
pub fn derive_xml_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
match &input.data {
syn::Data::Enum(e) => Enum::parse(&input, e).impl_se(),
syn::Data::Struct(s) => NamedStruct::parse(&input, s).impl_se(),
syn::Data::Union(_) => panic!("Union not supported"),
}
.into()
}
#[proc_macro_derive(XmlRoot, attributes(xml))]
pub fn derive_xml_root(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);

View File

@@ -2,11 +2,13 @@ use quick_xml::events::{BytesStart, Event};
use std::io::BufRead;
pub mod de;
pub mod se;
mod value;
pub use de::XmlDeError;
pub use de::XmlDeserialize;
pub use de::XmlRoot;
pub use se::XmlSerialize;
pub use value::Value;
impl<T: XmlDeserialize> XmlDeserialize for Option<T> {
@@ -42,33 +44,6 @@ impl XmlDeserialize for Unit {
}
}
impl XmlDeserialize for String {
fn deserialize<R: BufRead>(
reader: &mut quick_xml::NsReader<R>,
start: &BytesStart,
empty: bool,
) -> Result<Self, XmlDeError> {
if empty {
return Ok(String::new());
}
let mut content = String::new();
let mut buf = Vec::new();
loop {
match reader.read_event_into(&mut buf)? {
Event::End(e) if e.name() == start.name() => {
break;
}
Event::Eof => return Err(XmlDeError::Eof),
Event::Text(text) => {
content.push_str(&text.unescape()?);
}
_a => return Err(XmlDeError::UnknownError),
};
}
Ok(content)
}
}
// TODO: actually implement
pub struct Unparsed(BytesStart<'static>);

9
crates/xml/src/se.rs Normal file
View File

@@ -0,0 +1,9 @@
pub use xml_derive::XmlSerialize;
pub trait XmlSerialize {
fn serialize<W: std::io::Write>(
&self,
tag: Option<&[u8]>,
writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()>;
}

View File

@@ -1,5 +1,4 @@
use rustical_xml::{de::XmlRootParseStr, Unit, XmlDeserialize, XmlRoot};
use std::io::BufRead;
#[test]
fn test_struct_tagged_enum() {
@@ -15,11 +14,17 @@ fn test_struct_tagged_enum() {
prop: Vec<PropEnum>,
}
#[derive(Debug, XmlDeserialize, PartialEq)]
struct Displayname {
#[xml(ty = "text")]
name: String,
}
#[derive(Debug, XmlDeserialize, PartialEq)]
enum PropEnum {
A,
B,
Displayname(String),
Displayname(Displayname),
}
let doc = Propfind::parse_str(
@@ -41,7 +46,9 @@ fn test_struct_tagged_enum() {
prop: vec![
PropEnum::B,
PropEnum::A,
PropEnum::Displayname("Hello!".to_owned())
PropEnum::Displayname(Displayname {
name: "Hello!".to_owned()
})
]
}
}

View File

@@ -2,7 +2,6 @@ use rustical_xml::de::XmlRootParseStr;
use rustical_xml::XmlRoot;
use rustical_xml::{Unit, Unparsed, XmlDeserialize};
use std::collections::HashSet;
use std::io::BufRead;
#[test]
fn test_struct_text_field() {

View File

@@ -0,0 +1,29 @@
use rustical_xml::{XmlRoot, XmlSerialize};
use xml_derive::XmlDeserialize;
#[test]
fn test_struct_document() {
#[derive(Debug, XmlRoot, XmlSerialize, XmlDeserialize, PartialEq)]
#[xml(root = b"document")]
struct Document {
child: Child,
}
#[derive(Debug, XmlDeserialize, XmlSerialize, PartialEq, Default)]
struct Child {
#[xml(ty = "text")]
text: String,
}
let mut buf = Vec::new();
let mut writer = quick_xml::Writer::new(&mut buf);
Document {
child: Child {
text: "asd".to_owned(),
},
}
.serialize(Some(Document::root_tag()), &mut writer)
.unwrap();
let out = String::from_utf8(buf).unwrap();
dbg!(out);
}