WIP: Start implementing precondition errors

This commit is contained in:
Lennart
2025-06-04 20:03:30 +02:00
parent e57a14cad1
commit 1a827a164f
9 changed files with 239 additions and 167 deletions

View File

@@ -296,6 +296,12 @@ impl Field {
None => quote! { None },
};
let target = if let syn::Type::Reference(_) = self.field.ty {
quote! { (*self.#target_field_index) }
} else {
quote! { self.#target_field_index }
};
match (&self.attrs.xml_ty, self.attrs.flatten.is_present()) {
(FieldType::Attr, _) => None,
(FieldType::Text, true) => Some(quote! {
@@ -309,7 +315,7 @@ impl Field {
(FieldType::Tag, true) => {
let field_name = self.xml_name();
Some(quote! {
for item in self.#target_field_index.iter() {
for item in #target.iter() {
#serializer(item, #ns, Some(#field_name), namespaces, writer)?;
}
})
@@ -317,16 +323,16 @@ impl Field {
(FieldType::Tag, false) => {
let field_name = self.xml_name();
Some(quote! {
#serializer(&self.#target_field_index, #ns, Some(#field_name), namespaces, writer)?;
#serializer(&#target, #ns, Some(#field_name), namespaces, writer)?;
})
}
(FieldType::Untagged, true) => Some(quote! {
for item in self.#target_field_index.iter() {
for item in #target.iter() {
#serializer(item, None, None, namespaces, writer)?;
}
}),
(FieldType::Untagged, false) => Some(quote! {
#serializer(&self.#target_field_index, None, None, namespaces, writer)?;
#serializer(&#target, None, None, namespaces, writer)?;
}),
// We ignore this :)
(FieldType::TagName | FieldType::Namespace, _) => None,

View File

@@ -0,0 +1,150 @@
use quote::quote;
use crate::{Field, attrs::FieldType};
use super::NamedStruct;
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: Vec<_> = self.fields.iter().filter_map(Field::tag_writer).collect();
let untagged_attributes = self
.fields
.iter()
.filter(|field| field.attrs.xml_ty == FieldType::Untagged)
.filter(|field| !field.attrs.flatten.is_present())
.map(|field| {
let field_ident = field.target_field_index();
quote! {
if let Some(attrs) = self.#field_ident.attributes() {
bytes_start.extend_attributes(attrs);
}
}
});
let attributes = self
.fields
.iter()
.filter(|field| field.attrs.xml_ty == FieldType::Attr)
.map(|field| {
let field_name = field.xml_name();
let field_index = field.target_field_index();
quote! {
::quick_xml::events::attributes::Attribute {
key: ::quick_xml::name::QName(#field_name),
value: ::std::borrow::Cow::from(::rustical_xml::ValueSerialize::serialize(&self.#field_index).into_bytes())
}
}
});
let tag_name_field = self
.fields
.iter()
.find(|field| field.attrs.xml_ty == FieldType::TagName)
.map(|field| {
let field_index = field.target_field_index();
quote! {
let tag_str = self.#field_index.to_string();
let tag = Some(tag.unwrap_or(tag_str.as_bytes()));
}
});
let namespace_field = self
.fields
.iter()
.find(|field| field.attrs.xml_ty == FieldType::Namespace)
.map(|field| {
let field_index = field.target_field_index();
quote! {
let ns = self.#field_index;
}
});
let is_empty = tag_writers.is_empty();
// If we are the root element write the xmlns attributes
let prefix_attributes = if self.attrs.root.is_some() {
self.attrs
.ns_prefix
.iter()
.map(|(ns, prefix)| {
let sep = if !prefix.value().is_empty() {
b":".to_vec()
} else {
b"".to_vec()
};
let attr_name = [b"xmlns".as_ref(), &sep, &prefix.value()].concat();
let a = syn::LitByteStr::new(&attr_name, prefix.span());
quote! {
bytes_start.push_attribute((#a.as_ref(), #ns.as_ref()));
}
})
.collect()
} else {
vec![]
};
quote! {
impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause {
fn serialize<W: ::std::io::Write>(
&self,
ns: Option<::quick_xml::name::Namespace>,
tag: Option<&[u8]>,
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>,
writer: &mut ::quick_xml::Writer<W>
) -> ::std::io::Result<()> {
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
#tag_name_field;
#namespace_field;
let prefix = ns
.map(|ns| namespaces.get(&ns))
.unwrap_or(None)
.map(|prefix| {
if !prefix.is_empty() {
[*prefix, b":"].concat()
} else {
Vec::new()
}
});
let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname));
//
if let Some(qname) = &qname {
let mut bytes_start = BytesStart::from(qname.to_owned());
if !has_prefix {
if let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
}
}
#(#prefix_attributes);*
if let Some(attrs) = self.attributes() {
bytes_start.extend_attributes(attrs);
}
#(#untagged_attributes);*
if #is_empty {
writer.write_event(Event::Empty(bytes_start))?;
} else {
writer.write_event(Event::Start(bytes_start))?;
}
}
if !#is_empty {
#(#tag_writers);*
if let Some(qname) = &qname {
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?;
}
}
Ok(())
}
fn attributes<'a>(&self) -> Option<Vec<::quick_xml::events::attributes::Attribute<'a>>> {
Some(vec![ #(#attributes),* ])
}
}
}
}
}

View File

@@ -1,10 +1,12 @@
use crate::Field;
use crate::attrs::{FieldType, StructAttrs};
use crate::attrs::StructAttrs;
use core::panic;
use darling::FromDeriveInput;
use quote::quote;
use syn::{DataStruct, DeriveInput};
mod impl_se;
fn invalid_field_branch(ident: &syn::Ident, allow: bool) -> proc_macro2::TokenStream {
let ident = ident.to_string();
if allow {
@@ -190,147 +192,4 @@ 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: Vec<_> = self.fields.iter().filter_map(Field::tag_writer).collect();
let untagged_attributes = self
.fields
.iter()
.filter(|field| field.attrs.xml_ty == FieldType::Untagged)
.filter(|field| !field.attrs.flatten.is_present())
.map(|field| {
let field_ident = field.field_ident();
quote! {
if let Some(attrs) = self.#field_ident.attributes() {
bytes_start.extend_attributes(attrs);
}
}
});
let attributes = self
.fields
.iter()
.filter(|field| field.attrs.xml_ty == FieldType::Attr)
.map(|field| {
let field_name = field.xml_name();
let field_index = field.target_field_index();
quote! {
::quick_xml::events::attributes::Attribute {
key: ::quick_xml::name::QName(#field_name),
value: ::std::borrow::Cow::from(::rustical_xml::ValueSerialize::serialize(&self.#field_index).into_bytes())
}
}
});
let tag_name_field = self
.fields
.iter()
.find(|field| field.attrs.xml_ty == FieldType::TagName)
.map(|field| {
let field_index = field.target_field_index();
quote! {
let tag_str = self.#field_index.to_string();
let tag = Some(tag.unwrap_or(tag_str.as_bytes()));
}
});
let namespace_field = self
.fields
.iter()
.find(|field| field.attrs.xml_ty == FieldType::Namespace)
.map(|field| {
let field_index = field.target_field_index();
quote! {
let ns = self.#field_index;
}
});
let is_empty = tag_writers.is_empty();
// If we are the root element write the xmlns attributes
let prefix_attributes = if self.attrs.root.is_some() {
self.attrs
.ns_prefix
.iter()
.map(|(ns, prefix)| {
let sep = if !prefix.value().is_empty() {
b":".to_vec()
} else {
b"".to_vec()
};
let attr_name = [b"xmlns".as_ref(), &sep, &prefix.value()].concat();
let a = syn::LitByteStr::new(&attr_name, prefix.span());
quote! {
bytes_start.push_attribute((#a.as_ref(), #ns.as_ref()));
}
})
.collect()
} else {
vec![]
};
quote! {
impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause {
fn serialize<W: ::std::io::Write>(
&self,
ns: Option<::quick_xml::name::Namespace>,
tag: Option<&[u8]>,
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>,
writer: &mut ::quick_xml::Writer<W>
) -> ::std::io::Result<()> {
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
#tag_name_field;
#namespace_field;
let prefix = ns
.map(|ns| namespaces.get(&ns))
.unwrap_or(None)
.map(|prefix| {
if !prefix.is_empty() {
[*prefix, b":"].concat()
} else {
Vec::new()
}
});
let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname));
if let Some(qname) = &qname {
let mut bytes_start = BytesStart::from(qname.to_owned());
if !has_prefix {
if let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
}
}
#(#prefix_attributes);*
if let Some(attrs) = self.attributes() {
bytes_start.extend_attributes(attrs);
}
#(#untagged_attributes);*
if #is_empty {
writer.write_event(Event::Empty(bytes_start))?;
} else {
writer.write_event(Event::Start(bytes_start))?;
}
}
if !#is_empty {
#(#tag_writers);*
if let Some(qname) = &qname {
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?;
}
}
Ok(())
}
fn attributes<'a>(&self) -> Option<Vec<::quick_xml::events::attributes::Attribute<'a>>> {
Some(vec![ #(#attributes),* ])
}
}
}
}
}

View File

@@ -4,8 +4,8 @@ use std::io::BufRead;
pub use xml_derive::XmlDeserialize;
pub use xml_derive::XmlDocument;
use crate::XmlError;
use crate::XmlRootTag;
use crate::{XmlError, XmlSerialize};
pub trait XmlDeserialize: Sized {
fn deserialize<R: BufRead>(