mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 03:32:15 +00:00
WIP: Start implementing precondition errors
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use crate::error::Precondition;
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
use actix_web::HttpResponse;
|
use actix_web::HttpResponse;
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
@@ -60,16 +61,21 @@ pub async fn put_event<C: CalendarStore>(
|
|||||||
} = path.into_inner();
|
} = path.into_inner();
|
||||||
|
|
||||||
if !user.is_principal(&principal) {
|
if !user.is_principal(&principal) {
|
||||||
return Ok(HttpResponse::Unauthorized().body(""));
|
return Ok(HttpResponse::Unauthorized().finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
let overwrite =
|
let overwrite =
|
||||||
Some(&HeaderValue::from_static("*")) != req.headers().get(header::IF_NONE_MATCH);
|
Some(&HeaderValue::from_static("*")) != req.headers().get(header::IF_NONE_MATCH);
|
||||||
|
|
||||||
let object = CalendarObject::from_ics(object_id, body)?;
|
let object = match CalendarObject::from_ics(object_id, body) {
|
||||||
|
Ok(obj) => obj,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(Error::PreconditionFailed(Precondition::ValidCalendarData));
|
||||||
|
}
|
||||||
|
};
|
||||||
store
|
store
|
||||||
.put_object(principal, calendar_id, object, overwrite)
|
.put_object(principal, calendar_id, object, overwrite)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Created().body(""))
|
Ok(HttpResponse::Created().finish())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,36 @@
|
|||||||
use actix_web::{HttpResponse, http::StatusCode};
|
use actix_web::{
|
||||||
|
HttpResponse,
|
||||||
|
http::{StatusCode, header::ContentType},
|
||||||
|
};
|
||||||
|
use rustical_xml::{XmlSerialize, XmlSerializeRoot};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, XmlSerialize)]
|
||||||
|
pub enum Precondition {
|
||||||
|
#[error("valid-calendar-data")]
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
ValidCalendarData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl actix_web::ResponseError for Precondition {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
StatusCode::PRECONDITION_FAILED
|
||||||
|
}
|
||||||
|
fn error_response(&self) -> HttpResponse<actix_web::body::BoxBody> {
|
||||||
|
let mut output: Vec<_> = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".into();
|
||||||
|
let mut writer = quick_xml::Writer::new_with_indent(&mut output, b' ', 4);
|
||||||
|
|
||||||
|
let error = rustical_dav::xml::ErrorElement(self);
|
||||||
|
if let Err(err) = error.serialize_root(&mut writer) {
|
||||||
|
return rustical_dav::Error::from(err).error_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::PreconditionFailed()
|
||||||
|
.content_type(ContentType::xml())
|
||||||
|
.body(String::from_utf8(output).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Unauthorized")]
|
#[error("Unauthorized")]
|
||||||
@@ -26,6 +56,9 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IcalError(#[from] rustical_ical::Error),
|
IcalError(#[from] rustical_ical::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
PreconditionFailed(Precondition),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix_web::ResponseError for Error {
|
impl actix_web::ResponseError for Error {
|
||||||
@@ -44,6 +77,7 @@ impl actix_web::ResponseError for Error {
|
|||||||
Error::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR,
|
Error::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
Error::NotFound => StatusCode::NOT_FOUND,
|
Error::NotFound => StatusCode::NOT_FOUND,
|
||||||
Error::IcalError(err) => err.status_code(),
|
Error::IcalError(err) => err.status_code(),
|
||||||
|
Error::PreconditionFailed(err) => err.status_code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
|
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
|
||||||
@@ -51,6 +85,7 @@ impl actix_web::ResponseError for Error {
|
|||||||
match self {
|
match self {
|
||||||
Error::DavError(err) => err.error_response(),
|
Error::DavError(err) => err.error_response(),
|
||||||
Error::IcalError(err) => err.error_response(),
|
Error::IcalError(err) => err.error_response(),
|
||||||
|
Error::PreconditionFailed(err) => err.error_response(),
|
||||||
_ => HttpResponse::build(self.status_code()).body(self.to_string()),
|
_ => HttpResponse::build(self.status_code()).body(self.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
crates/dav/src/xml/error.rs
Normal file
12
crates/dav/src/xml/error.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
use rustical_xml::{XmlRootTag, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlSerialize, XmlRootTag)]
|
||||||
|
#[xml(ns = "crate::namespace::NS_DAV", root = b"error")]
|
||||||
|
#[xml(ns_prefix(
|
||||||
|
crate::namespace::NS_DAV = b"",
|
||||||
|
crate::namespace::NS_CARDDAV = b"CARD",
|
||||||
|
crate::namespace::NS_CALDAV = b"CAL",
|
||||||
|
crate::namespace::NS_CALENDARSERVER = b"CS",
|
||||||
|
crate::namespace::NS_DAVPUSH = b"PUSH"
|
||||||
|
))]
|
||||||
|
pub struct ErrorElement<'t, T: XmlSerialize>(#[xml(ty = "untagged")] pub &'t T);
|
||||||
14
crates/dav/src/xml/href.rs
Normal file
14
crates/dav/src/xml/href.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use derive_more::From;
|
||||||
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, Debug, Clone, From, PartialEq)]
|
||||||
|
pub struct HrefElement {
|
||||||
|
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||||
|
pub href: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HrefElement {
|
||||||
|
pub fn new(href: String) -> Self {
|
||||||
|
Self { href }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,22 +2,12 @@ pub mod multistatus;
|
|||||||
mod propfind;
|
mod propfind;
|
||||||
mod resourcetype;
|
mod resourcetype;
|
||||||
pub mod tag_list;
|
pub mod tag_list;
|
||||||
use derive_more::derive::From;
|
|
||||||
pub use multistatus::MultistatusElement;
|
pub use multistatus::MultistatusElement;
|
||||||
|
mod href;
|
||||||
|
pub use href::HrefElement;
|
||||||
pub use propfind::{PropElement, PropfindElement, PropfindType};
|
pub use propfind::{PropElement, PropfindElement, PropfindType};
|
||||||
pub use resourcetype::{Resourcetype, ResourcetypeInner};
|
pub use resourcetype::{Resourcetype, ResourcetypeInner};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
|
||||||
pub use tag_list::TagList;
|
pub use tag_list::TagList;
|
||||||
|
mod error;
|
||||||
pub mod sync_collection;
|
pub mod sync_collection;
|
||||||
|
pub use error::ErrorElement;
|
||||||
#[derive(XmlDeserialize, XmlSerialize, Debug, Clone, From, PartialEq)]
|
|
||||||
pub struct HrefElement {
|
|
||||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
|
||||||
pub href: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HrefElement {
|
|
||||||
pub fn new(href: String) -> Self {
|
|
||||||
Self { href }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -296,6 +296,12 @@ impl Field {
|
|||||||
None => quote! { None },
|
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()) {
|
match (&self.attrs.xml_ty, self.attrs.flatten.is_present()) {
|
||||||
(FieldType::Attr, _) => None,
|
(FieldType::Attr, _) => None,
|
||||||
(FieldType::Text, true) => Some(quote! {
|
(FieldType::Text, true) => Some(quote! {
|
||||||
@@ -309,7 +315,7 @@ impl Field {
|
|||||||
(FieldType::Tag, true) => {
|
(FieldType::Tag, true) => {
|
||||||
let field_name = self.xml_name();
|
let field_name = self.xml_name();
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
for item in self.#target_field_index.iter() {
|
for item in #target.iter() {
|
||||||
#serializer(item, #ns, Some(#field_name), namespaces, writer)?;
|
#serializer(item, #ns, Some(#field_name), namespaces, writer)?;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -317,16 +323,16 @@ impl Field {
|
|||||||
(FieldType::Tag, false) => {
|
(FieldType::Tag, false) => {
|
||||||
let field_name = self.xml_name();
|
let field_name = self.xml_name();
|
||||||
Some(quote! {
|
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! {
|
(FieldType::Untagged, true) => Some(quote! {
|
||||||
for item in self.#target_field_index.iter() {
|
for item in #target.iter() {
|
||||||
#serializer(item, None, None, namespaces, writer)?;
|
#serializer(item, None, None, namespaces, writer)?;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
(FieldType::Untagged, false) => Some(quote! {
|
(FieldType::Untagged, false) => Some(quote! {
|
||||||
#serializer(&self.#target_field_index, None, None, namespaces, writer)?;
|
#serializer(&#target, None, None, namespaces, writer)?;
|
||||||
}),
|
}),
|
||||||
// We ignore this :)
|
// We ignore this :)
|
||||||
(FieldType::TagName | FieldType::Namespace, _) => None,
|
(FieldType::TagName | FieldType::Namespace, _) => None,
|
||||||
|
|||||||
150
crates/xml/derive/src/xml_struct/impl_se.rs
Normal file
150
crates/xml/derive/src/xml_struct/impl_se.rs
Normal 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),* ])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
use crate::Field;
|
use crate::Field;
|
||||||
use crate::attrs::{FieldType, StructAttrs};
|
use crate::attrs::StructAttrs;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use darling::FromDeriveInput;
|
use darling::FromDeriveInput;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{DataStruct, DeriveInput};
|
use syn::{DataStruct, DeriveInput};
|
||||||
|
|
||||||
|
mod impl_se;
|
||||||
|
|
||||||
fn invalid_field_branch(ident: &syn::Ident, allow: bool) -> proc_macro2::TokenStream {
|
fn invalid_field_branch(ident: &syn::Ident, allow: bool) -> proc_macro2::TokenStream {
|
||||||
let ident = ident.to_string();
|
let ident = ident.to_string();
|
||||||
if allow {
|
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),* ])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@ use std::io::BufRead;
|
|||||||
pub use xml_derive::XmlDeserialize;
|
pub use xml_derive::XmlDeserialize;
|
||||||
pub use xml_derive::XmlDocument;
|
pub use xml_derive::XmlDocument;
|
||||||
|
|
||||||
use crate::XmlError;
|
|
||||||
use crate::XmlRootTag;
|
use crate::XmlRootTag;
|
||||||
|
use crate::{XmlError, XmlSerialize};
|
||||||
|
|
||||||
pub trait XmlDeserialize: Sized {
|
pub trait XmlDeserialize: Sized {
|
||||||
fn deserialize<R: BufRead>(
|
fn deserialize<R: BufRead>(
|
||||||
|
|||||||
Reference in New Issue
Block a user