mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 04:42:15 +00:00
xml: Add serialization
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
9
crates/xml/src/se.rs
Normal 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<()>;
|
||||
}
|
||||
@@ -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()
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
29
crates/xml/tests/se_struct.rs
Normal file
29
crates/xml/tests/se_struct.rs
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user