mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 10:32:19 +00:00
xml: Add serialization
This commit is contained in:
@@ -127,7 +127,7 @@ impl Enum {
|
|||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause {
|
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>,
|
reader: &mut quick_xml::NsReader<R>,
|
||||||
start: &quick_xml::events::BytesStart,
|
start: &quick_xml::events::BytesStart,
|
||||||
empty: bool
|
empty: bool
|
||||||
@@ -191,4 +191,32 @@ impl Enum {
|
|||||||
generics: input.generics.to_owned(),
|
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! {
|
quote! {
|
||||||
impl #impl_generics ::rustical_xml::XmlDeserialize for #ident #type_generics #where_clause {
|
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>,
|
reader: &mut quick_xml::NsReader<R>,
|
||||||
start: &quick_xml::events::BytesStart,
|
start: &quick_xml::events::BytesStart,
|
||||||
empty: bool
|
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()
|
.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))]
|
#[proc_macro_derive(XmlRoot, attributes(xml))]
|
||||||
pub fn derive_xml_root(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_xml_root(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ use quick_xml::events::{BytesStart, Event};
|
|||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
|
|
||||||
pub mod de;
|
pub mod de;
|
||||||
|
pub mod se;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use de::XmlDeError;
|
pub use de::XmlDeError;
|
||||||
pub use de::XmlDeserialize;
|
pub use de::XmlDeserialize;
|
||||||
pub use de::XmlRoot;
|
pub use de::XmlRoot;
|
||||||
|
pub use se::XmlSerialize;
|
||||||
pub use value::Value;
|
pub use value::Value;
|
||||||
|
|
||||||
impl<T: XmlDeserialize> XmlDeserialize for Option<T> {
|
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
|
// TODO: actually implement
|
||||||
pub struct Unparsed(BytesStart<'static>);
|
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 rustical_xml::{de::XmlRootParseStr, Unit, XmlDeserialize, XmlRoot};
|
||||||
use std::io::BufRead;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_struct_tagged_enum() {
|
fn test_struct_tagged_enum() {
|
||||||
@@ -15,11 +14,17 @@ fn test_struct_tagged_enum() {
|
|||||||
prop: Vec<PropEnum>,
|
prop: Vec<PropEnum>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||||
|
struct Displayname {
|
||||||
|
#[xml(ty = "text")]
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, XmlDeserialize, PartialEq)]
|
#[derive(Debug, XmlDeserialize, PartialEq)]
|
||||||
enum PropEnum {
|
enum PropEnum {
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
Displayname(String),
|
Displayname(Displayname),
|
||||||
}
|
}
|
||||||
|
|
||||||
let doc = Propfind::parse_str(
|
let doc = Propfind::parse_str(
|
||||||
@@ -41,7 +46,9 @@ fn test_struct_tagged_enum() {
|
|||||||
prop: vec![
|
prop: vec![
|
||||||
PropEnum::B,
|
PropEnum::B,
|
||||||
PropEnum::A,
|
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::XmlRoot;
|
||||||
use rustical_xml::{Unit, Unparsed, XmlDeserialize};
|
use rustical_xml::{Unit, Unparsed, XmlDeserialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::BufRead;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_struct_text_field() {
|
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