diff --git a/Cargo.lock b/Cargo.lock index e6d4e60..8a31d21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -892,6 +892,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "der" version = "0.7.9" @@ -1482,6 +1517,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -2582,7 +2623,6 @@ dependencies = [ name = "rustical_xml" version = "0.1.0" dependencies = [ - "heck", "quick-xml", "thiserror", "xml_derive", @@ -3860,6 +3900,7 @@ dependencies = [ name = "xml_derive" version = "0.1.0" dependencies = [ + "darling", "heck", "proc-macro2", "quote", diff --git a/crates/xml/Cargo.toml b/crates/xml/Cargo.toml index 235a94c..50e4e8a 100644 --- a/crates/xml/Cargo.toml +++ b/crates/xml/Cargo.toml @@ -6,7 +6,6 @@ description.workspace = true repository.workspace = true [dependencies] -heck = "0.5.0" quick-xml.workspace = true thiserror.workspace = true xml_derive = { path = "./derive" } diff --git a/crates/xml/derive/Cargo.toml b/crates/xml/derive/Cargo.toml index c606064..16910c5 100644 --- a/crates/xml/derive/Cargo.toml +++ b/crates/xml/derive/Cargo.toml @@ -12,3 +12,4 @@ quote = "1.0" proc-macro2 = "1.0" heck = "0.5.0" strum.workspace = true +darling = "0.20" diff --git a/crates/xml/derive/src/de/attrs.rs b/crates/xml/derive/src/de/attrs.rs index 8929aab..169a121 100644 --- a/crates/xml/derive/src/de/attrs.rs +++ b/crates/xml/derive/src/de/attrs.rs @@ -1,231 +1,48 @@ -use core::panic; +use darling::{util::Flag, FromDeriveInput, FromField, FromMeta, FromVariant}; +use syn::LitByteStr; -use heck::{ToKebabCase, ToPascalCase}; -use quote::ToTokens; -use strum::EnumString; -use syn::{ - punctuated::Punctuated, token::Comma, Attribute, Expr, ExprLit, Lit, LitByteStr, LitStr, Meta, -}; +#[derive(Default, FromMeta)] +pub struct ContainerAttrs { + pub ns_strict: Flag, +} -const ATTR_SCOPE: &str = "xml"; +#[derive(Default, FromMeta)] +pub struct TagAttrs { + pub rename: Option, + pub ns_strict: Flag, + pub ns: Option, +} -#[derive(Default)] +#[derive(Default, FromVariant)] +#[darling(attributes(xml))] pub struct VariantAttrs { - pub rename: Option, - pub ns: Option, + #[darling(flatten)] + pub common: TagAttrs, } -pub fn get_scoped_attrs(attrs: &[Attribute]) -> Option> { - attrs - .iter() - .find(|attr| attr.path().is_ident(ATTR_SCOPE)) - .map(|attr| { - attr.parse_args_with(Punctuated::::parse_terminated) - .unwrap() - }) -} - -pub fn parse_variant_attrs(attrs: &[Attribute]) -> VariantAttrs { - let mut variant_attrs = VariantAttrs::default(); - - let attrs = get_scoped_attrs(attrs); - - let attrs = if let Some(attrs) = attrs { - attrs - } else { - return variant_attrs; - }; - - for meta in attrs { - match meta { - // single flag - Meta::Path(path) => { - panic!("unrecognized variant flag: {}", path.to_token_stream()); - } - Meta::List(list) => { - panic!("list-type attrs not supported: {}", list.to_token_stream()); - } - Meta::NameValue(name_value) => { - if name_value.path.is_ident("ns") { - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = name_value.value - { - variant_attrs.ns = Some(lit_str.value()); - } - } else if name_value.path.is_ident("rename") { - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = name_value.value - { - variant_attrs.rename = Some(lit_str.value()); - } - } else { - panic!( - "unrecognized variant attribute: {}", - name_value.to_token_stream() - ); - } - } - } - } - variant_attrs -} - -#[derive(EnumString)] -pub enum CaseStyle { - #[strum(serialize = "kebab-case")] - KebabCase, - #[strum(serialize = "PascalCase")] - PascalCase, -} - -impl CaseStyle { - fn transform_text(&self, input: &str) -> String { - match self { - Self::KebabCase => input.to_kebab_case(), - Self::PascalCase => input.to_pascal_case(), - } - } -} - -#[derive(Default)] +#[derive(Default, FromDeriveInput)] +#[darling(attributes(xml))] pub struct EnumAttrs { - pub case_style: Option, - pub ns_strict: bool, + #[darling(flatten)] + container: ContainerAttrs, } -fn parse_enum_attrs(attrs: &[Attribute]) -> EnumAttrs { - let enum_attrs = EnumAttrs::default(); - - enum_attrs -} - -#[derive(Default)] +#[derive(Default, FromDeriveInput)] +#[darling(attributes(xml))] pub struct StructAttrs { + #[darling(flatten)] + pub container: ContainerAttrs, + pub root: Option, } -pub fn parse_struct_attrs(attrs: &[Attribute]) -> StructAttrs { - let mut struct_attrs = StructAttrs::default(); - - let attrs = get_scoped_attrs(attrs); - let attrs = if let Some(attrs) = attrs { - attrs - } else { - return struct_attrs; - }; - - for meta in attrs { - match meta { - // single flag - Meta::Path(_path) => { - panic!("invalid path attribute") - } - Meta::List(list) => { - panic!("list-type attrs not supported: {}", list.to_token_stream()); - } - Meta::NameValue(name_value) => { - if name_value.path.is_ident("root") { - if let Expr::Lit(ExprLit { - lit: Lit::ByteStr(lit_str), - .. - }) = name_value.value - { - struct_attrs.root = Some(lit_str); - } - } else { - panic!( - "unrecognized field attribute: {}", - name_value.to_token_stream() - ); - } - } - } - } - - struct_attrs -} - -#[derive(Default)] +#[derive(Default, FromField)] +#[darling(attributes(xml))] pub struct FieldAttrs { - pub rename: Option, - pub ns: Option, - pub text: bool, - pub untagged: bool, - pub flatten: bool, + #[darling(flatten)] + pub common: TagAttrs, + pub text: Flag, + pub untagged: Flag, + pub flatten: Flag, pub default: Option, } - -pub fn parse_field_attrs(attrs: &[Attribute]) -> FieldAttrs { - let mut field_attrs = FieldAttrs::default(); - - let attrs = get_scoped_attrs(attrs); - let attrs = if let Some(attrs) = attrs { - attrs - } else { - return field_attrs; - }; - - for meta in attrs { - match meta { - // single flag - Meta::Path(path) => { - if path.is_ident("text") { - field_attrs.text = true; - } - if path.is_ident("untagged") { - field_attrs.untagged = true; - } - if path.is_ident("flatten") { - field_attrs.flatten = true; - } - } - Meta::List(list) => { - panic!("list-type attrs not supported: {}", list.to_token_stream()); - } - Meta::NameValue(name_value) => { - if name_value.path.is_ident("ns") { - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = name_value.value - { - field_attrs.ns = Some(lit_str.value()); - } - } else if name_value.path.is_ident("rename") { - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = name_value.value - { - field_attrs.rename = Some(lit_str.value()); - } else { - panic!("invalid rename attribute"); - } - } else if name_value.path.is_ident("default") { - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = name_value.value - { - let a: syn::ExprPath = syn::parse_str(&lit_str.value()) - .expect("could not parse default attribute as expression"); - field_attrs.default = Some(a); - } else { - panic!("invalid default attribute"); - } - } else { - panic!( - "unrecognized field attribute: {}", - name_value.to_token_stream() - ); - } - } - } - } - - field_attrs -} diff --git a/crates/xml/derive/src/de/de_enum.rs b/crates/xml/derive/src/de/de_enum.rs index f5595c1..3e7f756 100644 --- a/crates/xml/derive/src/de/de_enum.rs +++ b/crates/xml/derive/src/de/de_enum.rs @@ -1,8 +1,10 @@ -use crate::de::attrs::parse_variant_attrs; -use proc_macro2::Span; +use darling::FromVariant; +use heck::ToKebabCase; use quote::quote; use syn::{DataEnum, DeriveInput, Fields, FieldsUnnamed, Variant}; +use crate::de::attrs::VariantAttrs; + pub fn enum_variant_branch(variant: &Variant) -> proc_macro2::TokenStream { let ident = &variant.ident; @@ -35,12 +37,13 @@ pub fn impl_de_enum(input: &DeriveInput, data: &DataEnum) -> proc_macro2::TokenS let name = &input.ident; let variants = data.variants.iter().map(|variant| { - let attrs = parse_variant_attrs(&variant.attrs); - - let variant_name = attrs.rename.unwrap_or(variant.ident.to_string()); - let variant_name = syn::LitByteStr::new(variant_name.as_bytes(), Span::call_site()); + let attrs = VariantAttrs::from_variant(variant).unwrap(); + let variant_name = attrs.common.rename.unwrap_or(syn::LitByteStr::new( + variant.ident.to_string().to_kebab_case().as_bytes(), + variant.ident.span(), + )); let branch = enum_variant_branch(variant); - // dbg!(variant.fields.to_token_stream()); + dbg!(&variant_name); quote! { #variant_name => { @@ -55,17 +58,16 @@ pub fn impl_de_enum(input: &DeriveInput, data: &DataEnum) -> proc_macro2::TokenS reader: &mut quick_xml::NsReader, start: &quick_xml::events::BytesStart, empty: bool - ) -> Result { + ) -> Result { use quick_xml::events::Event; let (_ns, name) = reader.resolve_element(start.name()); - match name.as_ref() { #(#variants)* name => { // Handle invalid variant name - Err(rustical_xml::XmlError::InvalidVariant(String::from_utf8_lossy(name).to_string())) + Err(rustical_xml::XmlDeError::InvalidVariant(String::from_utf8_lossy(name).to_string())) } } } diff --git a/crates/xml/derive/src/de/de_struct.rs b/crates/xml/derive/src/de/de_struct.rs index d219b8a..70d74fd 100644 --- a/crates/xml/derive/src/de/de_struct.rs +++ b/crates/xml/derive/src/de/de_struct.rs @@ -1,9 +1,11 @@ -use super::attrs::{parse_field_attrs, parse_struct_attrs, FieldAttrs}; +use crate::de::attrs::StructAttrs; + +use super::attrs::FieldAttrs; use core::panic; +use darling::{FromDeriveInput, FromField}; use heck::ToKebabCase; -use proc_macro2::Span; use quote::quote; -use syn::{AngleBracketedGenericArguments, DataStruct, DeriveInput, TypePath}; +use syn::{AngleBracketedGenericArguments, DataStruct, DeriveInput, LitByteStr, TypePath}; fn get_generic_type(ty: &syn::Type) -> Option<&syn::Type> { if let syn::Type::Path(TypePath { path, .. }) = ty { @@ -29,15 +31,19 @@ pub struct Field { impl Field { fn from_syn_field(field: syn::Field) -> Self { Self { - attrs: parse_field_attrs(&field.attrs), + attrs: FieldAttrs::from_field(&field).unwrap(), field, } } - fn de_name(&self) -> String { + fn de_name(&self) -> LitByteStr { self.attrs + .common .rename .to_owned() - .unwrap_or(self.field_ident().to_string().to_kebab_case()) + .unwrap_or(LitByteStr::new( + self.field_ident().to_string().to_kebab_case().as_bytes(), + self.field_ident().span(), + )) } fn field_ident(&self) -> &syn::Ident { @@ -58,7 +64,7 @@ impl Field { quote! { #field_ident: #ty, } - } else if self.attrs.flatten { + } else if self.attrs.flatten.is_present() { let generic_type = get_generic_type(ty).expect("flatten attribute only implemented for explicit generics (rustical_xml will assume the first generic as the inner type)"); quote! { #field_ident: Vec<#generic_type>, @@ -76,7 +82,7 @@ impl Field { quote! { #field_ident: #default(), } - } else if self.attrs.flatten { + } else if self.attrs.flatten.is_present() { quote! { #field_ident: vec![], } @@ -89,7 +95,7 @@ impl Field { fn builder_field_build(&self) -> proc_macro2::TokenStream { let field_ident = self.field_ident(); - if self.attrs.flatten { + if self.attrs.flatten.is_present() { quote! { #field_ident: FromIterator::from_iter(builder.#field_ident.into_iter()) } @@ -105,13 +111,13 @@ impl Field { } fn named_branch(&self) -> Option { - if self.attrs.text { + if self.attrs.text.is_present() { return None; } - if self.attrs.untagged { + if self.attrs.untagged.is_present() { return None; } - let field_name = syn::LitByteStr::new(self.de_name().as_bytes(), Span::call_site()); + let field_name = self.de_name(); let field_ident = self.field_ident(); let deserializer = self.ty(); Some(if self.attrs.default.is_some() { @@ -120,7 +126,7 @@ impl Field { builder.#field_ident = <#deserializer as rustical_xml::XmlDeserialize>::deserialize(reader, &start, empty)?; }, } - } else if self.attrs.flatten { + } else if self.attrs.flatten.is_present() { let deserializer = get_generic_type(self.ty()).expect("flatten attribute only implemented for explicit generics (rustical_xml will assume the first generic as the inner type)"); quote! { #field_name => { @@ -137,20 +143,29 @@ impl Field { } fn untagged_branch(&self) -> Option { - if !self.attrs.untagged { + if !self.attrs.untagged.is_present() { return None; } let field_ident = self.field_ident(); let deserializer = self.ty(); - Some(quote! { - _ => { - builder.#field_ident = Some(<#deserializer as rustical_xml::XmlDeserialize>::deserialize(reader, &start, empty)?); - }, + Some(if self.attrs.flatten.is_present() { + let deserializer = get_generic_type(self.ty()).expect("flatten attribute only implemented for explicit generics (rustical_xml will assume the first generic as the inner type)"); + quote! { + _ => { + builder.#field_ident.push(<#deserializer as rustical_xml::XmlDeserialize>::deserialize(reader, &start, empty)?); + }, + } + } else { + quote! { + _ => { + builder.#field_ident = Some(<#deserializer as rustical_xml::XmlDeserialize>::deserialize(reader, &start, empty)?); + }, + } }) } fn text_branch(&self) -> Option { - if !self.attrs.text { + if !self.attrs.text.is_present() { return None; } let field_ident = self.field_ident(); @@ -164,7 +179,7 @@ pub fn impl_de_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::To let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let name = &input.ident; - let struct_attrs = parse_struct_attrs(&input.attrs); + let struct_attrs = StructAttrs::from_derive_input(input).unwrap(); let fields: Vec<_> = data .fields @@ -203,9 +218,9 @@ pub fn impl_de_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::To reader: &mut quick_xml::NsReader, start: &quick_xml::events::BytesStart, empty: bool - ) -> Result { + ) -> Result { use quick_xml::events::Event; - use rustical_xml::XmlError; + use rustical_xml::XmlDeError; let mut buf = Vec::new(); @@ -225,7 +240,7 @@ pub fn impl_de_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::To Event::End(e) if e.name() == start.name() => { break; } - Event::Eof => return Err(XmlError::Eof), + Event::Eof => return Err(XmlDeError::Eof), // start of a child element Event::Start(start) | Event::Empty(start) => { let empty = matches!(event, Event::Empty(_)); @@ -235,7 +250,7 @@ pub fn impl_de_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::To #(#untagged_field_branches)* _ => { // invalid field name - return Err(XmlError::UnknownError) + return Err(XmlDeError::InvalidFieldName) } } } @@ -244,26 +259,26 @@ pub fn impl_de_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::To #(#text_field_branches)* } Event::CData(cdata) => { - return Err(XmlError::UnsupportedEvent("CDATA")); + return Err(XmlDeError::UnsupportedEvent("CDATA")); } Event::Comment(_) => { // ignore } Event::Decl(_) => { // Error: not supported - return Err(XmlError::UnsupportedEvent("Declaration")); + return Err(XmlDeError::UnsupportedEvent("Declaration")); } Event::PI(_) => { // Error: not supported - return Err(XmlError::UnsupportedEvent("Processing instruction")); + return Err(XmlDeError::UnsupportedEvent("Processing instruction")); } Event::DocType(doctype) => { // Error: start of new document - return Err(XmlError::UnsupportedEvent("Doctype in the middle of the document")); + return Err(XmlDeError::UnsupportedEvent("Doctype in the middle of the document")); } Event::End(end) => { // Error: premature end - return Err(XmlError::Other("Unexpected closing tag for wrong element".to_owned())); + return Err(XmlDeError::Other("Unexpected closing tag for wrong element".to_owned())); } } } diff --git a/crates/xml/src/de.rs b/crates/xml/src/de.rs new file mode 100644 index 0000000..cccb850 --- /dev/null +++ b/crates/xml/src/de.rs @@ -0,0 +1,80 @@ +use std::io::BufRead; +pub use xml_derive::XmlDeserialize; + +use quick_xml::events::{BytesStart, Event}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum XmlDeError { + #[error(transparent)] + QuickXmlDeError(#[from] quick_xml::de::DeError), + #[error(transparent)] + QuickXmlError(#[from] quick_xml::Error), + #[error("Unknown error")] + UnknownError, + #[error("Invalid tag {0}. Expected {1}")] + InvalidTag(String, String), + #[error("Missing field {0}")] + MissingField(&'static str), + #[error("End of file, expected closing tags")] + Eof, + #[error("Unsupported xml event: {0}")] + UnsupportedEvent(&'static str), + #[error("{0}")] + Other(String), + #[error("Invalid variant: {0}")] + InvalidVariant(String), + #[error("Invalid field name: ")] + InvalidFieldName, +} + +pub trait XmlDeserialize: Sized { + fn deserialize( + reader: &mut quick_xml::NsReader, + start: &BytesStart, + empty: bool, + ) -> Result; +} + +pub trait XmlRoot: XmlDeserialize { + fn parse(mut reader: quick_xml::NsReader) -> Result { + let mut buf = Vec::new(); + let event = reader.read_event_into(&mut buf)?; + match event { + Event::Start(start) => { + let (_ns, name) = reader.resolve_element(start.name()); + if name.as_ref() != Self::root_tag() { + return Err(XmlDeError::InvalidTag( + String::from_utf8_lossy(name.as_ref()).to_string(), + String::from_utf8_lossy(Self::root_tag()).to_string(), + )); + }; + // TODO: check namespace + + return Self::deserialize(&mut reader, &start, false); + } + Event::Empty(start) => { + let (_ns, name) = reader.resolve_element(start.name()); + if name.as_ref() != Self::root_tag() { + return Err(XmlDeError::InvalidTag( + String::from_utf8_lossy(name.as_ref()).to_string(), + String::from_utf8_lossy(Self::root_tag()).to_string(), + )); + }; + // TODO: check namespace + + return Self::deserialize(&mut reader, &start, true); + } + _ => {} + }; + Err(XmlDeError::UnknownError) + } + + fn parse_str(input: &str) -> Result { + let mut reader = quick_xml::NsReader::from_str(input); + reader.config_mut().trim_text(true); + Self::parse(reader) + } + + fn root_tag() -> &'static [u8]; +} diff --git a/crates/xml/src/lib.rs b/crates/xml/src/lib.rs index fb5c9ac..55b34af 100644 --- a/crates/xml/src/lib.rs +++ b/crates/xml/src/lib.rs @@ -1,87 +1,18 @@ use quick_xml::events::{BytesStart, Event}; use std::io::BufRead; -use thiserror::Error; -pub use xml_derive::XmlDeserialize; -#[derive(Debug, Error)] -pub enum XmlError { - #[error(transparent)] - QuickXmlDeError(#[from] quick_xml::de::DeError), - #[error(transparent)] - QuickXmlError(#[from] quick_xml::Error), - #[error("Unknown error")] - UnknownError, - #[error("Invalid tag {0}. Expected {1}")] - InvalidTag(String, String), - #[error("Missing field {0}")] - MissingField(&'static str), - #[error("End of file, expected closing tags")] - Eof, - #[error("Unsupported xml event: {0}")] - UnsupportedEvent(&'static str), - #[error("{0}")] - Other(String), - #[error("Invalid variant: {0}")] - InvalidVariant(String), -} +pub mod de; -pub trait XmlDeserialize: Sized { - fn deserialize( - reader: &mut quick_xml::NsReader, - start: &BytesStart, - empty: bool, - ) -> Result; -} - -pub trait XmlRoot: XmlDeserialize { - fn parse(mut reader: quick_xml::NsReader) -> Result { - let mut buf = Vec::new(); - let event = reader.read_event_into(&mut buf)?; - match event { - Event::Start(start) => { - let (_ns, name) = reader.resolve_element(start.name()); - if name.as_ref() != Self::root_tag() { - return Err(XmlError::InvalidTag( - String::from_utf8_lossy(name.as_ref()).to_string(), - String::from_utf8_lossy(Self::root_tag()).to_string(), - )); - }; - // TODO: check namespace - - return Self::deserialize(&mut reader, &start, false); - } - Event::Empty(start) => { - let (_ns, name) = reader.resolve_element(start.name()); - if name.as_ref() != Self::root_tag() { - return Err(XmlError::InvalidTag( - String::from_utf8_lossy(name.as_ref()).to_string(), - String::from_utf8_lossy(Self::root_tag()).to_string(), - )); - }; - // TODO: check namespace - - return Self::deserialize(&mut reader, &start, true); - } - _ => {} - }; - Err(XmlError::UnknownError) - } - - fn parse_str(input: &str) -> Result { - let mut reader = quick_xml::NsReader::from_str(input); - reader.config_mut().trim_text(true); - Self::parse(reader) - } - - fn root_tag() -> &'static [u8]; -} +pub use de::XmlDeError; +pub use de::XmlDeserialize; +pub use de::XmlRoot; impl XmlDeserialize for Option { fn deserialize( reader: &mut quick_xml::NsReader, start: &BytesStart, empty: bool, - ) -> Result { + ) -> Result { Ok(Some(T::deserialize(reader, start, empty)?)) } } @@ -93,7 +24,7 @@ impl XmlDeserialize for Unit { reader: &mut quick_xml::NsReader, start: &BytesStart, empty: bool, - ) -> Result { + ) -> Result { if empty { return Ok(Unit); } @@ -101,7 +32,7 @@ impl XmlDeserialize for Unit { loop { match reader.read_event_into(&mut buf)? { Event::End(e) if e.name() == start.name() => return Ok(Unit), - Event::Eof => return Err(XmlError::Eof), + Event::Eof => return Err(XmlDeError::Eof), _ => {} }; } @@ -113,7 +44,7 @@ impl XmlDeserialize for String { reader: &mut quick_xml::NsReader, start: &BytesStart, empty: bool, - ) -> Result { + ) -> Result { if empty { return Ok(String::new()); } @@ -124,11 +55,11 @@ impl XmlDeserialize for String { Event::End(e) if e.name() == start.name() => { break; } - Event::Eof => return Err(XmlError::Eof), + Event::Eof => return Err(XmlDeError::Eof), Event::Text(text) => { content.push_str(&text.unescape()?); } - _a => return Err(XmlError::UnknownError), + _a => return Err(XmlDeError::UnknownError), }; } Ok(content) diff --git a/crates/xml/tests/de_enum.rs b/crates/xml/tests/de_enum.rs index e1819ea..db3f540 100644 --- a/crates/xml/tests/de_enum.rs +++ b/crates/xml/tests/de_enum.rs @@ -4,6 +4,7 @@ use xml_derive::XmlDeserialize; #[test] fn test_struct_untagged_enum() { #[derive(Debug, XmlDeserialize, PartialEq)] + #[xml(root = b"propfind")] struct Propfind { prop: Prop, } @@ -20,12 +21,6 @@ fn test_struct_untagged_enum() { B, } - impl XmlRoot for Propfind { - fn root_tag() -> &'static [u8] { - b"propfind" - } - } - let doc = Propfind::parse_str( r#" diff --git a/crates/xml/tests/de_struct.rs b/crates/xml/tests/de_struct.rs index a97de1f..6722b40 100644 --- a/crates/xml/tests/de_struct.rs +++ b/crates/xml/tests/de_struct.rs @@ -62,8 +62,9 @@ fn test_struct_document() { #[test] fn test_struct_rename_field() { #[derive(Debug, XmlDeserialize, PartialEq)] + #[xml(root = b"document")] struct Document { - #[xml(rename = "ok-wow")] + #[xml(rename = b"ok-wow")] child: Child, } @@ -73,12 +74,6 @@ fn test_struct_rename_field() { text: String, } - impl XmlRoot for Document { - fn root_tag() -> &'static [u8] { - b"document" - } - } - let doc = Document::parse_str(r#"Hello!"#).unwrap(); assert_eq!( doc, @@ -93,17 +88,12 @@ fn test_struct_rename_field() { #[test] fn test_struct_optional_field() { #[derive(Debug, XmlDeserialize, PartialEq)] + #[xml(root = b"document")] struct Document { #[xml(default = "Default::default")] child: Option, } - impl XmlRoot for Document { - fn root_tag() -> &'static [u8] { - b"document" - } - } - #[derive(Debug, XmlDeserialize, PartialEq, Default)] struct Child; @@ -119,7 +109,7 @@ fn test_struct_vec() { #[derive(Debug, XmlDeserialize, PartialEq)] #[xml(root = b"document")] struct Document { - #[xml(rename = "child", flatten)] + #[xml(rename = b"child", flatten)] children: Vec, } @@ -147,7 +137,7 @@ fn test_struct_set() { #[derive(Debug, XmlDeserialize, PartialEq)] #[xml(root = b"document")] struct Document { - #[xml(rename = "child", flatten)] + #[xml(rename = b"child", flatten)] children: HashSet, } diff --git a/crates/xml/tests/propfind.rs b/crates/xml/tests/propfind.rs deleted file mode 100644 index a5a8166..0000000 --- a/crates/xml/tests/propfind.rs +++ /dev/null @@ -1,123 +0,0 @@ -use quick_xml::events::{BytesStart, Event}; -use rustical_xml::{Unit, XmlDeserialize, XmlError, XmlRoot}; - -#[derive(Debug, XmlDeserialize)] -#[xml(rename_all = "kebab-case")] -pub enum Prop { - #[xml(rename = "displayname")] - Displayname(String), - #[xml(ns = "DAV:Push", rename = "transports")] - Transports, -} - -#[derive(Debug)] -pub struct PropfindElement { - // child with name propfind and namespace DAV: - pub prop: Vec, - pub test: Option, -} - -impl XmlDeserialize for PropfindElement { - fn deserialize( - reader: &mut quick_xml::NsReader, - start: &BytesStart, - empty: bool, - ) -> Result { - // init values for the struct attributes - let mut attr_prop: Option> = None; - let mut attr_test: Option = None; - - if !empty { - 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(XmlError::Eof), - Event::Start(start) => { - let (_ns, name) = reader.resolve_element(start.name()); - match name.as_ref() { - b"prop" => { - if attr_prop.is_none() { - attr_prop = Some(Vec::::deserialize(reader, &start, false)?); - } - } - b"test" => { - if attr_test.is_none() { - attr_test = Some(Prop::deserialize(reader, &start, false)?); - } - } - _ => { - return Err(XmlError::InvalidTag( - String::from_utf8_lossy(name.as_ref()).to_string(), - "prop".to_string(), - )); - } - } - } - Event::Empty(start) => { - let (_ns, name) = reader.resolve_element(start.name()); - match name.as_ref() { - b"prop" => { - if attr_prop.is_none() { - attr_prop = Some(Vec::::deserialize(reader, &start, true)?); - } - } - b"test" => { - if attr_test.is_none() { - attr_test = Some(Prop::deserialize(reader, &start, true)?); - } - } - _ => { - return Err(XmlError::InvalidTag( - String::from_utf8_lossy(name.as_ref()).to_string(), - "prop".to_string(), - )); - } - } - } - a => { - dbg!(a); - } - }; - } - } - - let attr_prop = attr_prop.ok_or(XmlError::MissingField("prop"))?; - Ok(Self { - prop: attr_prop, - test: None, - }) - } -} - -impl XmlRoot for PropfindElement { - fn root_tag() -> &'static [u8] { - b"propfind" - } -} - -#[test] -fn test_propfind() { - let propfind: PropfindElement = PropfindElement::parse_str( - r#" - - - Hello! - - - - - Okay wow! - - - "#, - ) - .unwrap(); - dbg!(propfind); -} - -fn asd() { - let a: Option = None; -}