From b5a1915c7bafaa55910cc111a4f3e165442c0cc7 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Fri, 6 May 2022 17:23:23 -0500 Subject: [PATCH 01/15] WIP: implementing attribute parser and xmlwriter --- strong-xml-derive/src/read/mod.rs | 18 +++- strong-xml-derive/src/types.rs | 134 +++++++++++++++++++++++++++ strong-xml-derive/src/write/mod.rs | 20 +++- strong-xml-derive/src/write/named.rs | 49 +++++++--- strong-xml/src/xml_writer.rs | 30 ++++-- 5 files changed, 219 insertions(+), 32 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index 9e8103a..e573e9c 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -18,9 +18,13 @@ pub fn impl_read(element: Element) -> TokenStream { }); let read = variants.iter().map(|variant| match variant { - Fields::Named { tag, name, fields } => { - named::read(&tag, quote!(#ele_name::#name), &fields) - } + Fields::Named { + tag, + name, + fields, + prefix, + namespaces, + } => named::read(&tag, quote!(#ele_name::#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#ele_name::#name)), }); @@ -42,7 +46,13 @@ pub fn impl_read(element: Element) -> TokenStream { } Element::Struct { fields, .. } => match fields { - Fields::Named { tag, name, fields } => named::read(&tag, quote!(#name), &fields), + Fields::Named { + tag, + name, + fields, + prefix, + namespaces, + } => named::read(&tag, quote!(#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#name)), }, } diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index 0252f9e..66864e8 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{Lit::*, Meta::*, *}; @@ -31,6 +33,8 @@ pub enum Fields { tag: LitStr, name: Ident, fields: Vec, + prefix: Option, + namespaces: BTreeMap, LitStr>, }, /// Newtype struct or newtype variant /// @@ -49,6 +53,8 @@ pub enum Fields { tags: Vec, name: Ident, ty: Type, + prefix: Option, + namespaces: BTreeMap, LitStr>, }, } @@ -67,6 +73,7 @@ pub enum Field { ty: Type, tag: LitStr, default: bool, + prefix: Option, }, /// Child(ren) Field /// @@ -82,6 +89,7 @@ pub enum Field { ty: Type, default: bool, tags: Vec, + prefix: Option, }, /// Text Field /// @@ -160,6 +168,8 @@ impl Fields { pub fn parse(fields: syn::Fields, attrs: Vec, name: Ident) -> Fields { // Finding `tag` attribute let mut tags = Vec::new(); + let mut namespaces: BTreeMap, LitStr> = BTreeMap::default(); + let mut prefix = None; for meta in attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { @@ -170,6 +180,60 @@ impl Fields { panic!("Expected a string literal."); } } + NestedMeta::Meta(NameValue(m)) if m.path.is_ident("prefix") => { + if let Str(lit) = m.lit { + if prefix.is_some() { + panic!("Duplicate `ns` attribute."); + } else { + prefix = Some(lit); + } + } else { + panic!("Expected a string literal."); + } + } + NestedMeta::Meta(NameValue(m)) + if m.path + .get_ident() + .filter(|ident| ident.to_string().starts_with("xmlns")) + .is_some() => + { + let prefix = m + .path + .get_ident() + .unwrap() + .to_string() + .strip_prefix("xmlns:") + .map(|p| p.to_owned()); + + let namespace = if let Str(lit) = m.lit { + lit + } else { + panic!("Expected a string literal."); + }; + + if let Some(prefix) = &prefix { + if prefix.contains(":") { + panic!("prefix cannot contain `:`"); + } + + if prefix == "xml" + && namespace.value() != "http://www.w3.org/XML/1998/namespace" + { + panic!("xml prefix can only be bound to http://www.w3.org/XML/1998/namespace"); + } else if prefix.starts_with("xml") { + panic!("prefix cannot start with `xml`"); + } + } + + if namespaces.contains_key(&prefix) { + if let Some(ref prefix) = &prefix { + panic!("namespace {} already defined", prefix); + } else { + panic!("default namespace already defined"); + }; + } + namespaces.insert(prefix, namespace); + } _ => (), } } @@ -182,6 +246,8 @@ impl Fields { syn::Fields::Unit => Fields::Named { name, tag: tags.remove(0), + prefix, + namespaces, fields: Vec::new(), }, syn::Fields::Unnamed(fields) => { @@ -194,6 +260,8 @@ impl Fields { name, tags, ty: Type::parse(field.ty), + prefix, + namespaces, }; } } @@ -201,6 +269,8 @@ impl Fields { Fields::Named { name, tag: tags.remove(0), + namespaces, + prefix, fields: fields .unnamed .into_iter() @@ -216,6 +286,8 @@ impl Fields { syn::Fields::Named(_) => Fields::Named { name, tag: tags.remove(0), + namespaces, + prefix, fields: fields .into_iter() .map(|field| { @@ -237,6 +309,8 @@ impl Field { let mut is_text = false; let mut flatten_text_tag = None; let mut is_cdata = false; + let mut prefix = None; + let mut namespaces: BTreeMap, LitStr> = BTreeMap::default(); for meta in field.attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { @@ -324,6 +398,64 @@ impl Field { panic!("Expected a string literal."); } } + NestedMeta::Meta(NameValue(m)) if m.path.is_ident("prefix") => { + if let Str(lit) = m.lit { + if is_text { + panic!("`prefix` attribute and `text` attribute is disjoint."); + } else if flatten_text_tag.is_some() { + panic!("`prefix` attribute and `flatten_text` attribute is disjoint."); + } else if prefix.is_some() { + panic!("Duplicate `prefix` attribute."); + } else { + prefix = Some(lit); + } + } else { + panic!("Expected a string literal."); + } + } + NestedMeta::Meta(NameValue(m)) + if m.path + .get_ident() + .filter(|ident| ident.to_string().starts_with("xmlns")) + .is_some() => + { + let prefix = m + .path + .get_ident() + .unwrap() + .to_string() + .strip_prefix("xmlns:") + .map(|p| p.to_owned()); + + let namespace = if let Str(lit) = m.lit { + lit + } else { + panic!("Expected a string literal."); + }; + + if let Some(prefix) = &prefix { + if prefix.contains(":") { + panic!("prefix cannot contain `:`"); + } + + if prefix == "xml" + && namespace.value() != "http://www.w3.org/XML/1998/namespace" + { + panic!("xml prefix can only be bound to http://www.w3.org/XML/1998/namespace"); + } else if prefix.starts_with("xml") { + panic!("prefix cannot start with `xml`"); + } + } + + if namespaces.contains_key(&prefix) { + if let Some(ref prefix) = &prefix { + panic!("namespace {} already defined", prefix); + } else { + panic!("default namespace already defined"); + }; + } + namespaces.insert(prefix, namespace); + } _ => (), } } @@ -334,6 +466,7 @@ impl Field { bind, ty: Type::parse(field.ty), tag, + prefix, default, } } else if !child_tags.is_empty() { @@ -342,6 +475,7 @@ impl Field { bind, ty: Type::parse(field.ty), default, + prefix, tags: child_tags, } } else if is_text { diff --git a/strong-xml-derive/src/write/mod.rs b/strong-xml-derive/src/write/mod.rs index 7407b31..f37387a 100644 --- a/strong-xml-derive/src/write/mod.rs +++ b/strong-xml-derive/src/write/mod.rs @@ -26,9 +26,13 @@ pub fn impl_write(element: Element) -> TokenStream { }); let read = variants.iter().map(|variant| match variant { - Fields::Named { tag, name, fields } => { - named::write(&tag, quote!( #ele_name::#name ), &fields) - } + Fields::Named { + tag, + name, + fields, + prefix, + namespaces, + } => named::write(tag, prefix, quote!( #ele_name::#name ), &fields), Fields::Newtype { name, .. } => newtype::write(quote!( #ele_name::#name )), }); @@ -43,7 +47,13 @@ pub fn impl_write(element: Element) -> TokenStream { name: ele_name, fields, } => match fields { - Fields::Named { tag, name, fields } => { + Fields::Named { + tag, + name, + fields, + prefix, + namespaces, + } => { let bindings = fields.iter().map(|field| match field { Field::Attribute { bind, name, .. } | Field::Child { bind, name, .. } @@ -51,7 +61,7 @@ pub fn impl_write(element: Element) -> TokenStream { | Field::FlattenText { bind, name, .. } => quote!( #name: #bind ), }); - let read = named::write(&tag, quote!(#name), &fields); + let read = named::write(&tag, &prefix, quote!(#name), &fields); quote! { let #ele_name { #( #bindings ),* } = self; diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index 9d9fa00..71c5428 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -4,16 +4,17 @@ use syn::{Ident, LitStr}; use crate::types::{Field, Type}; -pub fn write(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStream { +pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, fields: &[Field]) -> TokenStream { + let write_attributes = fields.iter().filter_map(|field| match field { - Field::Attribute { tag, bind, ty, .. } => Some(write_attrs(&tag, &bind, &ty, &ele_name)), + Field::Attribute { tag, prefix, bind, ty, .. } => Some(write_attrs(&tag, prefix, &bind, &ty, &ele_name)), _ => None, }); let write_text = fields.iter().filter_map(|field| match field { Field::Text { - bind, ty, is_cdata, .. - } => Some(write_text(tag, bind, ty, &ele_name, *is_cdata)), + bind, ty, is_cdata,.. + } => Some(write_text(tag, prefix, bind, ty, &ele_name, *is_cdata)), _ => None, }); @@ -24,7 +25,7 @@ pub fn write(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStre ty, is_cdata, .. - } => Some(write_flatten_text(tag, bind, ty, &ele_name, *is_cdata)), + } => Some(write_flatten_text(tag, prefix, bind, ty, &ele_name, *is_cdata)), _ => None, }); @@ -71,7 +72,7 @@ pub fn write(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStre writer.write_element_end_open()?; #( #write_child )* #( #write_flatten_text )* - writer.write_element_end_close(#tag)?; + writer.write_element_end_close(#prefix, #tag)?; } } }; @@ -79,7 +80,7 @@ pub fn write(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStre quote! { strong_xml::log_start_writing!(#ele_name); - writer.write_element_start(#tag)?; + writer.write_element_start(#prefix, #tag)?; #( #write_attributes )* @@ -89,9 +90,15 @@ pub fn write(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStre } } -fn write_attrs(tag: &LitStr, name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { +fn write_attrs(tag: &LitStr, prefix: &Option, name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { let to_str = to_str(ty); + let prefix = if let Some(prefix) = prefix { + quote!(Some(#prefix)) + } else { + quote!(None) + }; + if ty.is_vec() { panic!("`attr` attribute doesn't support Vec."); } else if ty.is_option() { @@ -99,7 +106,7 @@ fn write_attrs(tag: &LitStr, name: &Ident, ty: &Type, ele_name: &TokenStream) -> strong_xml::log_start_writing_field!(#ele_name, #name); if let Some(__value) = #name { - writer.write_attribute(#tag, #to_str)?; + writer.write_attribute(#prefix, #tag, #to_str)?; } strong_xml::log_finish_writing_field!(#ele_name, #name); @@ -109,7 +116,7 @@ fn write_attrs(tag: &LitStr, name: &Ident, ty: &Type, ele_name: &TokenStream) -> strong_xml::log_start_writing_field!(#ele_name, #name); let __value = #name; - writer.write_attribute(#tag, #to_str)?; + writer.write_attribute(#prefix, #tag, #to_str)?; strong_xml::log_finish_writing_field!(#ele_name, #name); } @@ -149,6 +156,7 @@ fn write_child(name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { fn write_text( tag: &LitStr, + prefix: &Option, name: &Ident, ty: &Type, ele_name: &TokenStream, @@ -161,6 +169,12 @@ fn write_text( quote!(write_text) }; + let prefix = if let Some(prefix) = prefix { + quote!(Some(#prefix)) + } else { + quote!(None) + }; + quote! { writer.write_element_end_open()?; @@ -172,12 +186,13 @@ fn write_text( strong_xml::log_finish_writing_field!(#ele_name, #name); - writer.write_element_end_close(#tag)?; + writer.write_element_end_close(#prefix, #tag)?; } } fn write_flatten_text( tag: &LitStr, + prefix: &Option, name: &Ident, ty: &Type, ele_name: &TokenStream, @@ -185,12 +200,18 @@ fn write_flatten_text( ) -> TokenStream { let to_str = to_str(ty); + let prefix = if let Some(prefix) = prefix { + quote!(Some(#prefix)) + } else { + quote!(None) + }; + if ty.is_vec() { quote! { strong_xml::log_finish_writing_field!(#ele_name, #name); for __value in #name { - writer.write_flatten_text(#tag, #to_str, #is_cdata)?; + writer.write_flatten_text(#prefix, #tag, #to_str, #is_cdata)?; } strong_xml::log_finish_writing_field!(#ele_name, #name); @@ -200,7 +221,7 @@ fn write_flatten_text( strong_xml::log_finish_writing_field!(#ele_name, #name); if let Some(__value) = #name { - writer.write_flatten_text(#tag, #to_str, #is_cdata)?; + writer.write_flatten_text(#prefix, #tag, #to_str, #is_cdata)?; } strong_xml::log_finish_writing_field!(#ele_name, #name); @@ -210,7 +231,7 @@ fn write_flatten_text( strong_xml::log_finish_writing_field!(#ele_name, #name); let __value = &#name; - writer.write_flatten_text(#tag, #to_str, #is_cdata)?; + writer.write_flatten_text(#prefix, #tag, #to_str, #is_cdata)?; strong_xml::log_finish_writing_field!(#ele_name, #name); } diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index d1ad475..2fb4d21 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -16,12 +16,20 @@ impl XmlWriter { self.inner } - pub fn write_element_start(&mut self, tag: &str) -> Result<()> { - write!(self.inner, "<{}", tag) + pub fn write_element_start(&mut self, prefix: Option<&str>, tag: &str) -> Result<()> { + if let Some(prefix) = prefix { + write!(self.inner, "<{}:{}", prefix, tag) + } else { + write!(self.inner, "<{}", tag) + } } - pub fn write_attribute(&mut self, key: &str, value: &str) -> Result<()> { - write!(self.inner, r#" {}="{}""#, key, xml_escape(value)) + pub fn write_attribute(&mut self, prefix: Option<&str>, key: &str, value: &str) -> Result<()> { + if let Some(prefix) = prefix { + write!(self.inner, r#" {}:{}="{}""#, prefix, key, xml_escape(value)) + } else { + write!(self.inner, r#" {}="{}""#, key, xml_escape(value)) + } } pub fn write_text(&mut self, content: &str) -> Result<()> { @@ -36,20 +44,24 @@ impl XmlWriter { write!(self.inner, ">") } - pub fn write_flatten_text(&mut self, tag: &str, content: &str, is_cdata: bool) -> Result<()> { - self.write_element_start(tag)?; + pub fn write_flatten_text(&mut self, prefix: Option<&str>, tag: &str, content: &str, is_cdata: bool) -> Result<()> { + self.write_element_start(prefix, tag)?; self.write_element_end_open()?; if is_cdata { self.write_cdata_text(content)?; } else { self.write_text(content)?; } - self.write_element_end_close(tag)?; + self.write_element_end_close(prefix, tag)?; Ok(()) } - pub fn write_element_end_close(&mut self, tag: &str) -> Result<()> { - write!(self.inner, "", tag) + pub fn write_element_end_close(&mut self, prefix: Option<&str>, tag: &str) -> Result<()> { + if let Some(prefix) = prefix { + write!(self.inner, "", prefix, tag) + } else { + write!(self.inner, "", tag) + } } pub fn write_element_end_empty(&mut self) -> Result<()> { From 1e6b6a9c2d4484216a47a7bd5e2e3de5469982d8 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Fri, 6 May 2022 18:14:08 -0500 Subject: [PATCH 02/15] fixed borrow on unit --- strong-xml-derive/src/write/named.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index 71c5428..b677d6e 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -146,7 +146,7 @@ fn write_child(name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { Type::T(_) => quote! { strong_xml::log_start_writing_field!(#ele_name, #name); - &#name.to_writer(&mut writer)?; + #name.to_writer(&mut writer)?; strong_xml::log_finish_writing_field!(#ele_name, #name); }, From 2e5a2d8b4d9e4156c183aa21fa625476c6e31c90 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Mon, 9 May 2022 15:20:38 -0500 Subject: [PATCH 03/15] WIP: implementing attribute parser and xmlwriter --- strong-xml-derive/src/types.rs | 109 +++++++++++---------------- strong-xml-derive/src/write/named.rs | 6 ++ test-suite/tests/namespaces.rs | 30 ++++++++ 3 files changed, 78 insertions(+), 67 deletions(-) create mode 100644 test-suite/tests/namespaces.rs diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index 66864e8..eaa23d5 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,11 +1,13 @@ use std::collections::BTreeMap; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use syn::{Lit::*, Meta::*, *}; use crate::utils::elide_type_lifetimes; +type Namespaces = BTreeMap, String>; + pub enum Element { Struct { name: Ident, fields: Fields }, Enum { name: Ident, variants: Vec }, @@ -34,7 +36,7 @@ pub enum Fields { name: Ident, fields: Vec, prefix: Option, - namespaces: BTreeMap, LitStr>, + namespaces: Namespaces, }, /// Newtype struct or newtype variant /// @@ -54,7 +56,7 @@ pub enum Fields { name: Ident, ty: Type, prefix: Option, - namespaces: BTreeMap, LitStr>, + namespaces: Namespaces, }, } @@ -168,7 +170,7 @@ impl Fields { pub fn parse(fields: syn::Fields, attrs: Vec, name: Ident) -> Fields { // Finding `tag` attribute let mut tags = Vec::new(); - let mut namespaces: BTreeMap, LitStr> = BTreeMap::default(); + let mut namespaces: Namespaces = BTreeMap::default(); let mut prefix = None; for meta in attrs.into_iter().filter_map(get_xml_meta).flatten() { @@ -191,49 +193,57 @@ impl Fields { panic!("Expected a string literal."); } } - NestedMeta::Meta(NameValue(m)) - if m.path - .get_ident() - .filter(|ident| ident.to_string().starts_with("xmlns")) - .is_some() => - { - let prefix = m - .path - .get_ident() - .unwrap() - .to_string() - .strip_prefix("xmlns:") - .map(|p| p.to_owned()); - - let namespace = if let Str(lit) = m.lit { - lit + NestedMeta::Meta(NameValue(MetaNameValue { lit, path, .. })) if path.is_ident("ns") => { + + let (prefix, namespace) = if let Str(lit) = lit { + if let Some((pfx, ns)) = lit.value().split_once(':'){ + + (Some(pfx.to_string()), ns.to_string()) + } else { + (None, lit.value().to_string()) + } } else { panic!("Expected a string literal."); }; + if namespaces.contains_key(&prefix) { + if let Some(ref prefix) = prefix { + panic!("namespace {} already defined", prefix); + } else { + panic!("default namespace already defined"); + }; + } + + if let Some(prefix) = &prefix { if prefix.contains(":") { panic!("prefix cannot contain `:`"); } if prefix == "xml" - && namespace.value() != "http://www.w3.org/XML/1998/namespace" + && namespace != "http://www.w3.org/XML/1998/namespace" { panic!("xml prefix can only be bound to http://www.w3.org/XML/1998/namespace"); } else if prefix.starts_with("xml") { panic!("prefix cannot start with `xml`"); } } - - if namespaces.contains_key(&prefix) { - if let Some(ref prefix) = &prefix { - panic!("namespace {} already defined", prefix); - } else { - panic!("default namespace already defined"); - }; - } namespaces.insert(prefix, namespace); } + NestedMeta::Meta(NameValue(m)) + if m.path + .get_ident() + .filter(|ident| ident.to_string().starts_with("xmlns")) + .is_some() => + { + let prefix = m + .path + .get_ident() + .unwrap() + .to_string() + .strip_prefix("xmlns:") + .map(|p| p.to_owned()); + } _ => (), } } @@ -310,7 +320,7 @@ impl Field { let mut flatten_text_tag = None; let mut is_cdata = false; let mut prefix = None; - let mut namespaces: BTreeMap, LitStr> = BTreeMap::default(); + let mut namespaces: Namespaces = BTreeMap::default(); for meta in field.attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { @@ -416,45 +426,10 @@ impl Field { NestedMeta::Meta(NameValue(m)) if m.path .get_ident() - .filter(|ident| ident.to_string().starts_with("xmlns")) + .filter(|ident| ident.to_string().starts_with("ns")) .is_some() => { - let prefix = m - .path - .get_ident() - .unwrap() - .to_string() - .strip_prefix("xmlns:") - .map(|p| p.to_owned()); - - let namespace = if let Str(lit) = m.lit { - lit - } else { - panic!("Expected a string literal."); - }; - - if let Some(prefix) = &prefix { - if prefix.contains(":") { - panic!("prefix cannot contain `:`"); - } - - if prefix == "xml" - && namespace.value() != "http://www.w3.org/XML/1998/namespace" - { - panic!("xml prefix can only be bound to http://www.w3.org/XML/1998/namespace"); - } else if prefix.starts_with("xml") { - panic!("prefix cannot start with `xml`"); - } - } - - if namespaces.contains_key(&prefix) { - if let Some(ref prefix) = &prefix { - panic!("namespace {} already defined", prefix); - } else { - panic!("default namespace already defined"); - }; - } - namespaces.insert(prefix, namespace); + panic!("Namespace declaration not supported in this position"); } _ => (), } diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index b677d6e..1168e0a 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -60,6 +60,12 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field _ => None, }); + let prefix = if let Some(prefix) = prefix { + quote!(Some(#prefix)) + } else { + quote!(None) + }; + let write_element_end = if is_leaf_element { quote! { writer.write_element_end_empty()?; } } else if is_text_element { diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs new file mode 100644 index 0000000..1a35a73 --- /dev/null +++ b/test-suite/tests/namespaces.rs @@ -0,0 +1,30 @@ +use strong_xml::{XmlRead, XmlResult, XmlWrite}; + +#[derive(XmlWrite, XmlRead, PartialEq, Debug)] +#[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com")] +struct Nested { + #[xml(child = "nested")] + contents: Vec, +} + +#[test] +fn test() -> XmlResult<()> { + let _ = env_logger::builder() + .is_test(true) + .format_timestamp(None) + .try_init(); + + assert_eq!( + (Nested { + contents: vec![Nested { + contents: vec![Nested { + contents: vec![Nested { contents: vec![] }] + }] + }] + }) + .to_string()?, + r#""# + ); + + Ok(()) +} From 94d5b5247ea24d2848c5b0112ac1be3ad7813b27 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Tue, 10 May 2022 08:24:25 -0500 Subject: [PATCH 04/15] WIP: implementing attribute parser and xmlwriter --- strong-xml-derive/src/types.rs | 22 +------ strong-xml-derive/src/write/mod.rs | 4 +- strong-xml-derive/src/write/named.rs | 25 +++++++- strong-xml/src/xml_writer.rs | 89 +++++++++++++++++++++++++++- test-suite/tests/namespaces.rs | 2 +- 5 files changed, 116 insertions(+), 26 deletions(-) diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index eaa23d5..8f95f7a 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,12 +1,12 @@ use std::collections::BTreeMap; use proc_macro2::TokenStream; -use quote::{format_ident, quote, ToTokens}; +use quote::{format_ident, quote}; use syn::{Lit::*, Meta::*, *}; use crate::utils::elide_type_lifetimes; -type Namespaces = BTreeMap, String>; +pub type Namespaces = BTreeMap, String>; pub enum Element { Struct { name: Ident, fields: Fields }, @@ -196,8 +196,7 @@ impl Fields { NestedMeta::Meta(NameValue(MetaNameValue { lit, path, .. })) if path.is_ident("ns") => { let (prefix, namespace) = if let Str(lit) = lit { - if let Some((pfx, ns)) = lit.value().split_once(':'){ - + if let Some((pfx, ns)) = lit.value().split_once(": "){ (Some(pfx.to_string()), ns.to_string()) } else { (None, lit.value().to_string()) @@ -230,20 +229,6 @@ impl Fields { } namespaces.insert(prefix, namespace); } - NestedMeta::Meta(NameValue(m)) - if m.path - .get_ident() - .filter(|ident| ident.to_string().starts_with("xmlns")) - .is_some() => - { - let prefix = m - .path - .get_ident() - .unwrap() - .to_string() - .strip_prefix("xmlns:") - .map(|p| p.to_owned()); - } _ => (), } } @@ -320,7 +305,6 @@ impl Field { let mut flatten_text_tag = None; let mut is_cdata = false; let mut prefix = None; - let mut namespaces: Namespaces = BTreeMap::default(); for meta in field.attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { diff --git a/strong-xml-derive/src/write/mod.rs b/strong-xml-derive/src/write/mod.rs index f37387a..abf68ac 100644 --- a/strong-xml-derive/src/write/mod.rs +++ b/strong-xml-derive/src/write/mod.rs @@ -32,7 +32,7 @@ pub fn impl_write(element: Element) -> TokenStream { fields, prefix, namespaces, - } => named::write(tag, prefix, quote!( #ele_name::#name ), &fields), + } => named::write(tag, prefix, quote!( #ele_name::#name ), &fields, &namespaces), Fields::Newtype { name, .. } => newtype::write(quote!( #ele_name::#name )), }); @@ -61,7 +61,7 @@ pub fn impl_write(element: Element) -> TokenStream { | Field::FlattenText { bind, name, .. } => quote!( #name: #bind ), }); - let read = named::write(&tag, &prefix, quote!(#name), &fields); + let read = named::write(&tag, &prefix, quote!(#name), &fields, &namespaces); quote! { let #ele_name { #( #bindings ),* } = self; diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index 1168e0a..cf25429 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -2,9 +2,11 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{Ident, LitStr}; -use crate::types::{Field, Type}; +use crate::types::{Field, Type, Namespaces}; -pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, fields: &[Field]) -> TokenStream { +pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, fields: &[Field], namespaces: &Namespaces) -> TokenStream { + + let write_namespaces = write_namespaces(namespaces); let write_attributes = fields.iter().filter_map(|field| match field { Field::Attribute { tag, prefix, bind, ty, .. } => Some(write_attrs(&tag, prefix, &bind, &ty, &ele_name)), @@ -87,7 +89,8 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field strong_xml::log_start_writing!(#ele_name); writer.write_element_start(#prefix, #tag)?; - + + #write_namespaces #( #write_attributes )* #write_element_end @@ -96,6 +99,8 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field } } + + fn write_attrs(tag: &LitStr, prefix: &Option, name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { let to_str = to_str(ty); @@ -129,6 +134,20 @@ fn write_attrs(tag: &LitStr, prefix: &Option, name: &Ident, ty: &Type, e } } + +fn write_namespaces(namespaces: &Namespaces) -> TokenStream { + namespaces.iter().map(|(prefix, ns)|{ + let prefix = if let Some(prefix) = prefix { + quote!(Some(#prefix)) + } else { + quote!(None) + }; + quote! { + writer.write_namespace_declaration(#prefix, #ns)?; + } + }).collect() +} + fn write_child(name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { match ty { Type::OptionT(_) => quote! { diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index 2fb4d21..05c6360 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; +use std::collections::BTreeSet; use std::io::Result; use std::io::Write; @@ -5,11 +7,17 @@ use crate::xml_escape::xml_escape; pub struct XmlWriter { pub inner: W, + pub used_namespaces: BTreeMap, Vec<&'static str>>, + pub set_prefixes: Vec>> } impl XmlWriter { pub fn new(inner: W) -> Self { - XmlWriter { inner } + XmlWriter { + inner, + used_namespaces: BTreeMap::new(), + set_prefixes: Vec::new(), + } } pub fn into_inner(self) -> W { @@ -17,6 +25,7 @@ impl XmlWriter { } pub fn write_element_start(&mut self, prefix: Option<&str>, tag: &str) -> Result<()> { + self.set_prefixes.push(BTreeSet::new()); if let Some(prefix) = prefix { write!(self.inner, "<{}:{}", prefix, tag) } else { @@ -24,7 +33,22 @@ impl XmlWriter { } } + pub fn write_namespace_declaration(&mut self, prefix: Option<&'static str>, ns: &'static str) -> Result<()> { + if !self.is_prefix_defined_as(&prefix, ns) { + self.push_changed_namespace(prefix, ns)?; + if let Some(prefix) = prefix { + write!(self.inner, r#" xmlns:{}="{}""#, prefix, xml_escape(ns)) + } else { + write!(self.inner, r#" xmlns="{}""#, xml_escape(ns)) + } + } else { + Ok(()) + } + } + pub fn write_attribute(&mut self, prefix: Option<&str>, key: &str, value: &str) -> Result<()> { + + if let Some(prefix) = prefix { write!(self.inner, r#" {}:{}="{}""#, prefix, key, xml_escape(value)) } else { @@ -57,6 +81,7 @@ impl XmlWriter { } pub fn write_element_end_close(&mut self, prefix: Option<&str>, tag: &str) -> Result<()> { + self.pop_changed_namespaces()?; if let Some(prefix) = prefix { write!(self.inner, "", prefix, tag) } else { @@ -65,6 +90,68 @@ impl XmlWriter { } pub fn write_element_end_empty(&mut self) -> Result<()> { + self.pop_changed_namespaces()?; write!(self.inner, "/>") } + + pub fn is_prefix_defined_as(&mut self, prefix: &Option<&str>, namespace: &str) -> bool { + match self.used_namespaces.get(prefix) { + Some(scope) => scope.last() == Some(&namespace), + _ => false, + } + } + + pub fn get_namespace(&mut self, prefix: &Option<&str>) -> Option<&'static str> { + match self.used_namespaces.get(prefix) { + Some(scope) => { + if let Some(&namespace) = scope.last() { + Some(namespace) + } else { + None + } + } + _ => None + } + } + + fn pop_changed_namespaces(&mut self) -> Result<()> { + use std::io::{Error, ErrorKind}; + if let Some(set_prefixes) = self.set_prefixes.pop() { + set_prefixes.iter().map(|pfx| -> Result<()> { + match self.used_namespaces.get_mut(pfx) { + Some(v) => { + if let Some(_) = v.pop() { + Ok(()) + } else { + Err(Error::new(ErrorKind::Other, "Prefix state could not be popped")) + } + } + None => Err(Error::new(ErrorKind::Other, "Prefix does not exist in scope")) + } + }).collect::>>()?; + Ok(()) + } else { + Err(Error::new(ErrorKind::Other, "Failed to restore previous prefix scope")) + } + } + + fn push_changed_namespace(&mut self, prefix: Option<&'static str>, namespace: &'static str) -> Result<()> { + use std::io::{Error, ErrorKind}; + + let set_prefixes = if let Some(blah) = self.set_prefixes.last_mut() { + blah + } else { + return Err(Error::new(ErrorKind::Other, "Failed to get current prefix scope")); + }; + + + if let Some(v) = self.used_namespaces.get_mut(&prefix) { + v.push(namespace); + } else { + self.used_namespaces.insert(prefix, vec![namespace]); + } + set_prefixes.insert(prefix); + + Ok(()) + } } diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index 1a35a73..36abe99 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -23,7 +23,7 @@ fn test() -> XmlResult<()> { }] }) .to_string()?, - r#""# + r#""# ); Ok(()) From 712d85b97e753e3fac861ca0f3ee326507b9bb09 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Tue, 10 May 2022 08:41:18 -0500 Subject: [PATCH 05/15] WIP: added multiple namespace test --- test-suite/tests/namespaces.rs | 55 +++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index 36abe99..3a08288 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -1,14 +1,16 @@ use strong_xml::{XmlRead, XmlResult, XmlWrite}; -#[derive(XmlWrite, XmlRead, PartialEq, Debug)] -#[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com")] -struct Nested { - #[xml(child = "nested")] - contents: Vec, -} - #[test] fn test() -> XmlResult<()> { + + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] + #[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com")] + struct Nested { + #[xml(child = "nested")] + contents: Vec, + } + + let _ = env_logger::builder() .is_test(true) .format_timestamp(None) @@ -28,3 +30,42 @@ fn test() -> XmlResult<()> { Ok(()) } + + + +#[test] +fn test2() -> XmlResult<()> { + + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] + #[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com", ns = "n2: http://www.example.com")] + struct Nested { + #[xml(child="nested")] + nested: Vec, + #[xml(child="nested2")] + nested2: Nested2 + } + + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] + #[xml(tag = "nested2", prefix = "n2", ns = "n2: http://www.example.com")] + struct Nested2 { + #[xml(attr="nest", prefix = "n2")] + value: String + } + + + let _ = env_logger::builder() + .is_test(true) + .format_timestamp(None) + .try_init(); + + assert_eq!( + (Nested { + nested: vec![], + nested2: Nested2 { value: "hello world".into() } + }) + .to_string()?, + r#""# + ); + + Ok(()) +} From 7d7f5c67de568eead9be733cb947e4d740454b10 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Wed, 11 May 2022 06:46:37 -0500 Subject: [PATCH 06/15] wip: working on reader and writer --- strong-xml-derive/src/read/mod.rs | 30 ++++-- strong-xml-derive/src/read/named.rs | 68 +++++++----- strong-xml-derive/src/types.rs | 16 +-- strong-xml-derive/src/write/mod.rs | 8 +- strong-xml/src/log.rs | 16 ++- strong-xml/src/xml_reader.rs | 155 ++++++++++++++++++---------- strong-xml/src/xml_writer.rs | 72 ++++++++----- 7 files changed, 240 insertions(+), 125 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index e573e9c..7c83140 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -5,6 +5,7 @@ use crate::types::{Element, Fields}; use proc_macro2::TokenStream; use quote::quote; +use syn::LitStr; pub fn impl_read(element: Element) -> TokenStream { match element { @@ -12,10 +13,19 @@ pub fn impl_read(element: Element) -> TokenStream { name: ele_name, variants, } => { - let tags = variants.iter().map(|variant| match variant { - Fields::Newtype { tags, .. } => tags.clone(), - Fields::Named { tag, .. } => vec![tag.clone()], - }); + let make_prefix = |prefix: &Option| prefix.clone(); + + let (prefixes, locals): (Vec<_>, Vec<_>) = variants + .iter() + .map(|variant| match variant { + Fields::Newtype { prefix, tags, .. } => { + (vec![make_prefix(prefix); tags.len()], tags.clone()) + } + Fields::Named { prefix, tag, .. } => { + (vec![make_prefix(prefix)], vec![tag.clone()]) + } + }) + .unzip(); let read = variants.iter().map(|variant| match variant { Fields::Named { @@ -24,19 +34,19 @@ pub fn impl_read(element: Element) -> TokenStream { fields, prefix, namespaces, - } => named::read(&tag, quote!(#ele_name::#name), &fields), + } => named::read(&prefix, &tag, quote!(#ele_name::#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#ele_name::#name)), }); quote! { while let Some(tag) = reader.find_element_start(None)? { match tag { - #( #( #tags )|* => { #read } )* - tag => { - strong_xml::log_skip_element!(#ele_name, tag); + #( #( (#prefixes, #locals) )|* => { #read } )* + (prefix, local) => { + strong_xml::log_skip_element!(#ele_name, prefix, local); // skip the start tag reader.next(); - reader.read_to_end(tag)?; + reader.read_to_end(prefix, local)?; }, } } @@ -52,7 +62,7 @@ pub fn impl_read(element: Element) -> TokenStream { fields, prefix, namespaces, - } => named::read(&tag, quote!(#name), &fields), + } => named::read(&prefix, &tag, quote!(#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#name)), }, } diff --git a/strong-xml-derive/src/read/named.rs b/strong-xml-derive/src/read/named.rs index 59bf50f..a455380 100644 --- a/strong-xml-derive/src/read/named.rs +++ b/strong-xml-derive/src/read/named.rs @@ -4,7 +4,12 @@ use syn::{Ident, LitStr}; use crate::types::{Field, Type}; -pub fn read(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStream { +pub fn read( + prefix: &Option, + local: &LitStr, + ele_name: TokenStream, + fields: &[Field], +) -> TokenStream { let init_fields = fields.iter().map(|field| match field { Field::Attribute { bind, ty, .. } | Field::Child { bind, ty, .. } @@ -41,10 +46,11 @@ pub fn read(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStrea Field::Attribute { bind, ty, + prefix, tag, name, .. - } => Some(read_attrs(&tag, &bind, &name, &ty, &ele_name)), + } => Some(read_attrs(&prefix, &tag, &bind, &name, &ty, &ele_name)), _ => None, }); @@ -63,15 +69,18 @@ pub fn read(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStrea Field::FlattenText { bind, ty, + prefix, tag, name, .. - } => Some(read_flatten_text(tag, bind, name, ty, &ele_name)), + } => Some(read_flatten_text(prefix, tag, bind, name, ty, &ele_name)), _ => None, }); let read_text_fields = fields.iter().filter_map(|field| match field { - Field::Text { bind, ty, name, .. } => Some(read_text(&tag, bind, name, ty, &ele_name)), + Field::Text { bind, ty, name, .. } => { + Some(read_text(&prefix, &local, bind, name, ty, &ele_name)) + } _ => None, }); @@ -89,6 +98,12 @@ pub fn read(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStrea return Ok(__res); }; + let prefix = if let Some(lit) = prefix { + quote!(#lit) + } else { + quote!("") + }; + let read_content = if is_text_element { quote! { #( #read_text_fields )* @@ -100,15 +115,15 @@ pub fn read(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStrea #return_fields } - while let Some(__tag) = reader.find_element_start(Some(#tag))? { - match __tag { + while let Some((__prefix, __local)) = reader.find_element_start(Some(#prefix, #local))? { + match (__prefix, __local) { #( #read_child_fields, )* #( #read_flatten_text_fields, )* - tag => { - strong_xml::log_skip_element!(#ele_name, tag); + (prefix, local) => { + strong_xml::log_skip_element!(#ele_name, prefix, local); // skip the start tag reader.next(); - reader.read_to_end(tag)?; + reader.read_to_end(prefix, local)?; }, } } @@ -122,13 +137,13 @@ pub fn read(tag: &LitStr, ele_name: TokenStream, fields: &[Field]) -> TokenStrea #( #init_fields )* - reader.read_till_element_start(#tag)?; + reader.read_till_element_start(#prefix, #local)?; - while let Some((__key, __value)) = reader.find_attribute()? { - match __key { + while let Some((__prefix, __key, __value)) = reader.find_attribute()? { + match (__prefix, __key) { #( #read_attr_fields, )* - key => { - strong_xml::log_skip_attribute!(#ele_name, key); + (prefix, key) => { + strong_xml::log_skip_attribute!(#ele_name, prefix, key); }, } } @@ -167,7 +182,8 @@ fn return_value( } fn read_attrs( - tag: &LitStr, + prefix: &Option, + local: &LitStr, bind: &Ident, name: &TokenStream, ty: &Type, @@ -179,7 +195,7 @@ fn read_attrs( panic!("`attr` attribute doesn't support Vec."); } else { quote! { - #tag => { + => { strong_xml::log_start_reading_field!(#ele_name, #name); #bind = Some(#from_str); @@ -191,7 +207,8 @@ fn read_attrs( } fn read_text( - tag: &LitStr, + prefix: &Option, + local: &LitStr, bind: &Ident, name: &TokenStream, ty: &Type, @@ -205,7 +222,7 @@ fn read_text( quote! { strong_xml::log_start_reading_field!(#ele_name, #name); - let __value = reader.read_text(#tag)?; + let __value = reader.read_text(#prefix, #local)?; #bind = Some(#from_str); strong_xml::log_finish_reading_field!(#ele_name, #name); @@ -214,7 +231,7 @@ fn read_text( } fn read_children( - tags: &[LitStr], + tags: &[(Option, LitStr)], bind: &Ident, name: &TokenStream, ty: &Type, @@ -230,8 +247,10 @@ fn read_children( _ => panic!("`child` attribute only supports Vec, Option and T."), }; + let (prefixes, locals): (Vec<_>, Vec<_>) = tags.iter().cloned().unzip(); + quote! { - #( #tags )|* => { + #( (#prefixes, #locals) )|* => { strong_xml::log_start_reading_field!(#ele_name, #name); #from_reader @@ -242,7 +261,8 @@ fn read_children( } fn read_flatten_text( - tag: &LitStr, + prefix: &Option, + local: &LitStr, bind: &Ident, name: &TokenStream, ty: &Type, @@ -252,18 +272,18 @@ fn read_flatten_text( let read_text = if ty.is_vec() { quote! { - let __value = reader.read_text(#tag)?; + let __value = reader.read_text(#prefix, #local)?; #bind.push(#from_str); } } else { quote! { - let __value = reader.read_text(#tag)?; + let __value = reader.read_text(#prefix, #local)?; #bind = Some(#from_str); } }; quote! { - #tag => { + (#prefix, #local) => { // skip element start reader.next(); diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index 8f95f7a..18ec7af 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -120,6 +120,7 @@ pub enum Field { bind: Ident, ty: Type, default: bool, + prefix: Option, tag: LitStr, is_cdata: bool, }, @@ -193,10 +194,11 @@ impl Fields { panic!("Expected a string literal."); } } - NestedMeta::Meta(NameValue(MetaNameValue { lit, path, .. })) if path.is_ident("ns") => { - + NestedMeta::Meta(NameValue(MetaNameValue { lit, path, .. })) + if path.is_ident("ns") => + { let (prefix, namespace) = if let Str(lit) = lit { - if let Some((pfx, ns)) = lit.value().split_once(": "){ + if let Some((pfx, ns)) = lit.value().split_once(": ") { (Some(pfx.to_string()), ns.to_string()) } else { (None, lit.value().to_string()) @@ -212,16 +214,13 @@ impl Fields { panic!("default namespace already defined"); }; } - - + if let Some(prefix) = &prefix { if prefix.contains(":") { panic!("prefix cannot contain `:`"); } - if prefix == "xml" - && namespace != "http://www.w3.org/XML/1998/namespace" - { + if prefix == "xml" && namespace != "http://www.w3.org/XML/1998/namespace" { panic!("xml prefix can only be bound to http://www.w3.org/XML/1998/namespace"); } else if prefix.starts_with("xml") { panic!("prefix cannot start with `xml`"); @@ -450,6 +449,7 @@ impl Field { bind, ty: Type::parse(field.ty), default, + prefix, tag, is_cdata, } diff --git a/strong-xml-derive/src/write/mod.rs b/strong-xml-derive/src/write/mod.rs index abf68ac..a6b90bf 100644 --- a/strong-xml-derive/src/write/mod.rs +++ b/strong-xml-derive/src/write/mod.rs @@ -32,7 +32,13 @@ pub fn impl_write(element: Element) -> TokenStream { fields, prefix, namespaces, - } => named::write(tag, prefix, quote!( #ele_name::#name ), &fields, &namespaces), + } => named::write( + tag, + prefix, + quote!( #ele_name::#name ), + &fields, + &namespaces, + ), Fields::Newtype { name, .. } => newtype::write(quote!( #ele_name::#name )), }); diff --git a/strong-xml/src/log.rs b/strong-xml/src/log.rs index c394429..5135ade 100644 --- a/strong-xml/src/log.rs +++ b/strong-xml/src/log.rs @@ -45,10 +45,14 @@ macro_rules! log_finish_reading_field { #[macro_export] #[doc(hidden)] macro_rules! log_skip_attribute { - ($element:path, $key:ident) => { + ($element:path, $prefix:ident, $local:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip attribute `{}`"), - $key + if $prefix.is_empty() { + $local + } else { + &($prefix.to_owned() + ":" + $local) + } ); }; } @@ -56,10 +60,14 @@ macro_rules! log_skip_attribute { #[macro_export] #[doc(hidden)] macro_rules! log_skip_element { - ($element:path, $tag:ident) => { + ($element:path, $prefix:ident, $local:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip element `{}`"), - $tag + if $prefix.is_empty() { + $local + } else { + &($prefix.to_owned() + ":" + $local) + } ); }; } diff --git a/strong-xml/src/xml_reader.rs b/strong-xml/src/xml_reader.rs index eb84505..020b1fe 100644 --- a/strong-xml/src/xml_reader.rs +++ b/strong-xml/src/xml_reader.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::fmt::format; use std::iter::{Iterator, Peekable}; use xmlparser::ElementEnd; @@ -17,6 +18,16 @@ pub struct XmlReader<'a> { tokenizer: Peekable>, } +/// Xml Nma +/// +/// Holds both the namespace prefix and the local name +/// of a node or attribute +#[derive(PartialEq, Eq)] +pub struct XmlName<'a> { + pub prefix: &'a str, + pub local: &'a str, +} + impl<'a> XmlReader<'a> { #[inline] pub fn new(text: &'a str) -> XmlReader<'a> { @@ -36,7 +47,20 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn read_text(&mut self, end_tag: &str) -> XmlResult> { + fn make_name<'r>(prefix: &'r str, local: &'r str) -> Cow<'r, str> { + if prefix.is_empty() { + Cow::Borrowed(local) + } else { + Cow::Owned(prefix.to_owned() + ":" + local) + } + } + + #[inline] + pub fn read_text( + &mut self, + end_prefix: &'a str, + end_local: &'a str, + ) -> XmlResult> { let mut res = None; while let Some(token) = self.next() { @@ -53,17 +77,15 @@ impl<'a> XmlReader<'a> { res = Some(Cow::Borrowed(text.as_str())); } Token::ElementEnd { - end: ElementEnd::Close(_, _), - span, + end: ElementEnd::Close(prefix, local), + .. } => { - let span = span.as_str(); // - let tag = &span[2..span.len() - 1]; // remove `` - if end_tag == tag { + if prefix.as_str() == end_prefix && local.as_str() == end_local { break; } else { return Err(XmlError::TagMismatch { - expected: end_tag.to_owned(), - found: tag.to_owned(), + expected: Self::make_name(end_prefix, end_local).to_string(), + found: Self::make_name(prefix.as_str(), local.as_str()).to_string(), }); } } @@ -79,15 +101,14 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn read_till_element_start(&mut self, end_tag: &str) -> XmlResult<()> { + pub fn read_till_element_start(&mut self, end_prefix: &str, end_local: &str) -> XmlResult<()> { while let Some(token) = self.next() { match token? { - Token::ElementStart { span, .. } => { - let tag = &span.as_str()[1..]; - if end_tag == tag { + Token::ElementStart { prefix, local, .. } => { + if prefix.as_str() == end_prefix && local.as_str() == end_local { break; } else { - self.read_to_end(tag)?; + self.read_to_end(prefix.as_str(), local.as_str())?; } } Token::ElementEnd { .. } @@ -105,16 +126,20 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn find_attribute(&mut self) -> XmlResult)>> { + pub fn find_attribute(&mut self) -> XmlResult)>> { if let Some(token) = self.tokenizer.peek() { match token { - Ok(Token::Attribute { span, value, .. }) => { - let value = value.as_str(); - let span = span.as_str(); // key="value" - let key = &span[0..span.len() - value.len() - 3]; // remove `="`, value and `"` - let value = Cow::Borrowed(value); + Ok(Token::Attribute { + prefix, + local, + value, + .. + }) => { + let prefix = prefix.as_str(); + let local = local.as_str(); + let value = Cow::Borrowed(value.as_str()); self.next(); - return Ok(Some((key, value))); + return Ok(Some((prefix, local, value))); } Ok(Token::ElementEnd { end: ElementEnd::Open, @@ -140,26 +165,27 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn find_element_start(&mut self, end_tag: Option<&str>) -> XmlResult> { + pub fn find_element_start( + &mut self, + end_tag: Option<(&str, &str)>, + ) -> XmlResult> { while let Some(token) = self.tokenizer.peek() { match token { - Ok(Token::ElementStart { span, .. }) => { - return Ok(Some(&span.as_str()[1..])); + Ok(Token::ElementStart { prefix, local, .. }) => { + return Ok(Some((prefix.as_str(), local.as_str()))); } Ok(Token::ElementEnd { - end: ElementEnd::Close(_, _), - span, + end: ElementEnd::Close(prefix, local), + .. }) if end_tag.is_some() => { - let end_tag = end_tag.unwrap(); - let span = span.as_str(); // - let tag = &span[2..span.len() - 1]; // remove `` - if tag == end_tag { + let (end_prefix, end_local) = end_tag.unwrap(); + if prefix.as_str() == end_prefix && local.as_str() == end_local { self.next(); return Ok(None); } else { return Err(XmlError::TagMismatch { - expected: end_tag.to_owned(), - found: tag.to_owned(), + expected: Self::make_name(end_prefix, end_local).to_string(), + found: Self::make_name(prefix.as_str(), local.as_str()).to_string(), }); } } @@ -174,12 +200,11 @@ impl<'a> XmlReader<'a> { } } } - Err(XmlError::UnexpectedEof) } #[inline] - pub fn read_to_end(&mut self, end_tag: &str) -> XmlResult<()> { + pub fn read_to_end(&mut self, end_prefix: &str, end_local: &str) -> XmlResult<()> { while let Some(token) = self.next() { match token? { // if this element is emtpy, just return @@ -205,7 +230,9 @@ impl<'a> XmlReader<'a> { while let Some(token) = self.next() { match token? { - Token::ElementStart { span, .. } if end_tag == &span.as_str()[1..] => { + Token::ElementStart { prefix, local, .. } + if prefix.as_str() == end_prefix && local.as_str() == end_local => + { while let Some(token) = self.next() { match token? { Token::ElementEnd { @@ -237,9 +264,9 @@ impl<'a> XmlReader<'a> { } } Token::ElementEnd { - end: ElementEnd::Close(_, _), - span, - } if end_tag == &span.as_str()[2..span.as_str().len() - 1] => { + end: ElementEnd::Close(prefix, local), + .. + } if prefix.as_str() == end_prefix && local.as_str() == end_local => { depth -= 1; if depth == 0 { return Ok(()); @@ -253,61 +280,79 @@ impl<'a> XmlReader<'a> { } } +impl<'a> XmlName<'a> { + #[inline] + pub fn new(prefix: &'a str, local: &'a str) -> XmlName<'a> { + XmlName { prefix, local } + } + + #[inline] + pub fn cmp_raw

(&self, prefix: P, local: &'a str) -> bool + where + P: Into>, + { + // this make the method able to be called like + // cmp_raw(None, "local") or cmp_raw("prefix", "local") + let prefix: Option<_> = prefix.into(); + (prefix == None && self.prefix == "") || prefix == Some(self.prefix) && local == self.local + } +} + #[test] fn read_text() -> XmlResult<()> { let mut reader = XmlReader::new(""); assert!(reader.next().is_some()); // "text"); assert!(reader.next().is_some()); // "text"); assert!(reader.next().is_some()); // ""'<>&"); assert!(reader.next().is_some()); // "&"#); + assert_eq!(reader.read_text("", "parent")?, r#""'<>&"#); assert!(reader.next().is_none()); let mut reader = XmlReader::new(""); assert!(reader.next().is_some()); // ""); assert!(reader.next().is_some()); // ""); assert!(reader.next().is_some()); // "]]>"); assert!(reader.next().is_some()); // ""); + assert_eq!(reader.read_text("", "parent")?, ""); assert!(reader.next().is_none()); reader = XmlReader::new(""); assert!(reader.next().is_some()); // " XmlResult<()> { fn read_till_element_start() -> XmlResult<()> { let mut reader = XmlReader::new(""); - reader.read_till_element_start("tag")?; + reader.read_till_element_start("", "tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_none()); @@ -325,7 +370,7 @@ fn read_till_element_start() -> XmlResult<()> { assert!(reader.next().is_some()); // "" - reader.read_till_element_start("tag")?; + reader.read_till_element_start("", "tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -334,7 +379,7 @@ fn read_till_element_start() -> XmlResult<()> { assert!(reader.next().is_some()); // "" - reader.read_till_element_start("tag")?; + reader.read_till_element_start("", "tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -343,7 +388,7 @@ fn read_till_element_start() -> XmlResult<()> { assert!(reader.next().is_some()); // "" - reader.read_till_element_start("tag")?; + reader.read_till_element_start("", "tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -352,7 +397,7 @@ fn read_till_element_start() -> XmlResult<()> { assert!(reader.next().is_some()); // "" - reader.read_till_element_start("tag")?; + reader.read_till_element_start("", "tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -367,7 +412,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -376,7 +421,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -385,7 +430,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -394,7 +439,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index 05c6360..5cc1523 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -8,12 +8,12 @@ use crate::xml_escape::xml_escape; pub struct XmlWriter { pub inner: W, pub used_namespaces: BTreeMap, Vec<&'static str>>, - pub set_prefixes: Vec>> + pub set_prefixes: Vec>>, } impl XmlWriter { pub fn new(inner: W) -> Self { - XmlWriter { + XmlWriter { inner, used_namespaces: BTreeMap::new(), set_prefixes: Vec::new(), @@ -33,7 +33,11 @@ impl XmlWriter { } } - pub fn write_namespace_declaration(&mut self, prefix: Option<&'static str>, ns: &'static str) -> Result<()> { + pub fn write_namespace_declaration( + &mut self, + prefix: Option<&'static str>, + ns: &'static str, + ) -> Result<()> { if !self.is_prefix_defined_as(&prefix, ns) { self.push_changed_namespace(prefix, ns)?; if let Some(prefix) = prefix { @@ -41,14 +45,12 @@ impl XmlWriter { } else { write!(self.inner, r#" xmlns="{}""#, xml_escape(ns)) } - } else { + } else { Ok(()) } } pub fn write_attribute(&mut self, prefix: Option<&str>, key: &str, value: &str) -> Result<()> { - - if let Some(prefix) = prefix { write!(self.inner, r#" {}:{}="{}""#, prefix, key, xml_escape(value)) } else { @@ -68,7 +70,13 @@ impl XmlWriter { write!(self.inner, ">") } - pub fn write_flatten_text(&mut self, prefix: Option<&str>, tag: &str, content: &str, is_cdata: bool) -> Result<()> { + pub fn write_flatten_text( + &mut self, + prefix: Option<&str>, + tag: &str, + content: &str, + is_cdata: bool, + ) -> Result<()> { self.write_element_start(prefix, tag)?; self.write_element_end_open()?; if is_cdata { @@ -110,48 +118,66 @@ impl XmlWriter { None } } - _ => None + _ => None, } } fn pop_changed_namespaces(&mut self) -> Result<()> { use std::io::{Error, ErrorKind}; if let Some(set_prefixes) = self.set_prefixes.pop() { - set_prefixes.iter().map(|pfx| -> Result<()> { - match self.used_namespaces.get_mut(pfx) { - Some(v) => { - if let Some(_) = v.pop() { - Ok(()) - } else { - Err(Error::new(ErrorKind::Other, "Prefix state could not be popped")) + set_prefixes + .iter() + .map(|pfx| -> Result<()> { + match self.used_namespaces.get_mut(pfx) { + Some(v) => { + if let Some(_) = v.pop() { + Ok(()) + } else { + Err(Error::new( + ErrorKind::Other, + "Prefix state could not be popped", + )) + } } + None => Err(Error::new( + ErrorKind::Other, + "Prefix does not exist in scope", + )), } - None => Err(Error::new(ErrorKind::Other, "Prefix does not exist in scope")) - } - }).collect::>>()?; + }) + .collect::>>()?; Ok(()) } else { - Err(Error::new(ErrorKind::Other, "Failed to restore previous prefix scope")) + Err(Error::new( + ErrorKind::Other, + "Failed to restore previous prefix scope", + )) } } - fn push_changed_namespace(&mut self, prefix: Option<&'static str>, namespace: &'static str) -> Result<()> { + fn push_changed_namespace( + &mut self, + prefix: Option<&'static str>, + namespace: &'static str, + ) -> Result<()> { use std::io::{Error, ErrorKind}; let set_prefixes = if let Some(blah) = self.set_prefixes.last_mut() { blah } else { - return Err(Error::new(ErrorKind::Other, "Failed to get current prefix scope")); + return Err(Error::new( + ErrorKind::Other, + "Failed to get current prefix scope", + )); }; - if let Some(v) = self.used_namespaces.get_mut(&prefix) { v.push(namespace); } else { self.used_namespaces.insert(prefix, vec![namespace]); } set_prefixes.insert(prefix); - + Ok(()) } } From d3c22155bb75b3fe5585afbc13dd6fe445af6ca2 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Wed, 11 May 2022 09:11:00 -0500 Subject: [PATCH 07/15] wip: compiles and tests run, ns behavior not sane --- strong-xml-derive/src/read/mod.rs | 8 ++++- strong-xml-derive/src/read/named.rs | 45 +++++++++++++++++++++++++---- strong-xml/src/log.rs | 25 +++++++++------- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index 7c83140..579fe7b 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -13,7 +13,13 @@ pub fn impl_read(element: Element) -> TokenStream { name: ele_name, variants, } => { - let make_prefix = |prefix: &Option| prefix.clone(); + let make_prefix = |prefix: &Option| { + if let Some(lit) = prefix { + quote!(#lit) + } else { + quote!("") + } + }; let (prefixes, locals): (Vec<_>, Vec<_>) = variants .iter() diff --git a/strong-xml-derive/src/read/named.rs b/strong-xml-derive/src/read/named.rs index a455380..f86ff5d 100644 --- a/strong-xml-derive/src/read/named.rs +++ b/strong-xml-derive/src/read/named.rs @@ -61,7 +61,20 @@ pub fn read( tags, name, .. - } => Some(read_children(tags, bind, name, ty, &ele_name)), + } => { + let tags: Vec<_> = tags.iter().map(|tag|{ + if tag.value().matches(":").count() > 1 { + panic!("child cannot have more than one colon in name.") + } + match tag.value().split_once(':') { + Some(("", local)) => (None, local.to_owned()), + Some((prefix, local)) => (Some(prefix.to_owned()), local.to_owned()), + None => (None, tag.value()) + } + }).collect(); + + Some(read_children(&tags[..], bind, name, ty, &ele_name)) + } _ => None, }); @@ -115,7 +128,7 @@ pub fn read( #return_fields } - while let Some((__prefix, __local)) = reader.find_element_start(Some(#prefix, #local))? { + while let Some((__prefix, __local)) = reader.find_element_start(Some((#prefix, #local)))? { match (__prefix, __local) { #( #read_child_fields, )* #( #read_flatten_text_fields, )* @@ -191,11 +204,17 @@ fn read_attrs( ) -> TokenStream { let from_str = from_str(ty); + let prefix = if let Some(lit) = prefix { + quote!(#lit) + } else { + quote!("") + }; + if ty.is_vec() { panic!("`attr` attribute doesn't support Vec."); } else { quote! { - => { + (#prefix, #local) => { strong_xml::log_start_reading_field!(#ele_name, #name); #bind = Some(#from_str); @@ -215,6 +234,12 @@ fn read_text( ele_name: &TokenStream, ) -> TokenStream { let from_str = from_str(ty); + + let prefix = if let Some(lit) = prefix { + quote!(#lit) + } else { + quote!("") + }; if ty.is_vec() { panic!("`text` attribute doesn't support Vec."); @@ -231,7 +256,7 @@ fn read_text( } fn read_children( - tags: &[(Option, LitStr)], + tags: &[(Option, String)], bind: &Ident, name: &TokenStream, ty: &Type, @@ -248,7 +273,11 @@ fn read_children( }; let (prefixes, locals): (Vec<_>, Vec<_>) = tags.iter().cloned().unzip(); - + let prefixes = prefixes.iter().map(|prefix| if let Some(lit) = prefix { + quote!(#lit) + } else { + quote!("") + }); quote! { #( (#prefixes, #locals) )|* => { strong_xml::log_start_reading_field!(#ele_name, #name); @@ -270,6 +299,12 @@ fn read_flatten_text( ) -> TokenStream { let from_str = from_str(ty); + let prefix = if let Some(lit) = prefix { + quote!(#lit) + } else { + quote!("") + }; + let read_text = if ty.is_vec() { quote! { let __value = reader.read_text(#prefix, #local)?; diff --git a/strong-xml/src/log.rs b/strong-xml/src/log.rs index 5135ade..801ba30 100644 --- a/strong-xml/src/log.rs +++ b/strong-xml/src/log.rs @@ -42,17 +42,26 @@ macro_rules! log_finish_reading_field { }; } + +#[macro_export] +#[doc(hidden)] +macro_rules! make_tag { + ($prefix:ident, $local:ident) => { + if $prefix.is_empty() { + $local.to_owned() + } else { + ($prefix.to_owned() + ":" + $local) + } + }; +} + #[macro_export] #[doc(hidden)] macro_rules! log_skip_attribute { ($element:path, $prefix:ident, $local:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip attribute `{}`"), - if $prefix.is_empty() { - $local - } else { - &($prefix.to_owned() + ":" + $local) - } + $crate::make_tag!($prefix, $local) ); }; } @@ -63,11 +72,7 @@ macro_rules! log_skip_element { ($element:path, $prefix:ident, $local:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip element `{}`"), - if $prefix.is_empty() { - $local - } else { - &($prefix.to_owned() + ":" + $local) - } + $crate::make_tag!($prefix, $local) ); }; } From f9daf9a75d415b5e802908840922136dbcabc973 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Wed, 11 May 2022 17:35:14 -0500 Subject: [PATCH 08/15] Old tests pass, time to rewrite closer to original --- test-suite/tests/namespaces.rs | 52 ++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index 3a08288..01f79be 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -39,9 +39,9 @@ fn test2() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com", ns = "n2: http://www.example.com")] struct Nested { - #[xml(child="nested")] + #[xml(child="nested", prefix = "n")] nested: Vec, - #[xml(child="nested2")] + #[xml(child="nested2", prefix = "n2")] nested2: Nested2 } @@ -67,5 +67,53 @@ fn test2() -> XmlResult<()> { r#""# ); + + /* + assert_eq!( + (Nested { + nested: vec![], + nested2: Nested2 { value: "hello world".into() } + }), + Nested::from_str(r#""#)? + ); + */ + + + + + + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] + #[xml(tag = "tag", prefix = "a", ns = "a: http://www.example.com")] + struct A { + #[xml(text)] + value: String + } + + + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] + #[xml(tag = "tag", prefix = "b", ns = "b: http://www.example.com/1")] + struct B { + #[xml(text)] + value: String + } + + + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] + #[xml(tag = "root", prefix = "a", ns = "a: ns_a", ns = "b: ns_b")] + struct C { + #[xml(child="tag", prefix = "a")] + a: A, + #[xml(child="tag", prefix = "b")] + b: B + } + + assert_eq!( + C{a:A{value: "hello".into()}, b: B{value: "world".into()}}, + C::from_str(r#"helloworld"#)? + ); + + + + Ok(()) } From 809c0a7356493a443237b61369835886567de2fd Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Wed, 11 May 2022 18:29:55 -0500 Subject: [PATCH 09/15] Old tests pass, time to rewrite closer to original From ecea269a5a0d871ea46505499a1d80adab612fb4 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Thu, 12 May 2022 14:11:25 -0500 Subject: [PATCH 10/15] WIP: rewriting closer to original --- strong-xml-derive/src/read/mod.rs | 36 ++----- strong-xml-derive/src/read/named.rs | 101 +++++-------------- strong-xml-derive/src/types.rs | 144 +++++++++++++++++---------- strong-xml-derive/src/write/mod.rs | 5 +- strong-xml-derive/src/write/named.rs | 60 ++++------- strong-xml/src/log.rs | 8 +- strong-xml/src/xml_reader.rs | 142 +++++++++++--------------- strong-xml/src/xml_writer.rs | 36 ++----- test-suite/tests/bool.rs | 1 + test-suite/tests/enum.rs | 5 + 10 files changed, 229 insertions(+), 309 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index 579fe7b..ab1ef86 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -13,46 +13,33 @@ pub fn impl_read(element: Element) -> TokenStream { name: ele_name, variants, } => { - let make_prefix = |prefix: &Option| { - if let Some(lit) = prefix { - quote!(#lit) - } else { - quote!("") - } - }; - - let (prefixes, locals): (Vec<_>, Vec<_>) = variants + + let tags = variants .iter() .map(|variant| match variant { - Fields::Newtype { prefix, tags, .. } => { - (vec![make_prefix(prefix); tags.len()], tags.clone()) - } - Fields::Named { prefix, tag, .. } => { - (vec![make_prefix(prefix)], vec![tag.clone()]) - } - }) - .unzip(); + Fields::Newtype { tags, .. } => tags.iter().cloned().collect(), + Fields::Named { tag, .. } => vec!(tag.clone()) + }); let read = variants.iter().map(|variant| match variant { Fields::Named { tag, name, fields, - prefix, namespaces, - } => named::read(&prefix, &tag, quote!(#ele_name::#name), &fields), + } => named::read(&tag, quote!(#ele_name::#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#ele_name::#name)), }); quote! { while let Some(tag) = reader.find_element_start(None)? { match tag { - #( #( (#prefixes, #locals) )|* => { #read } )* - (prefix, local) => { - strong_xml::log_skip_element!(#ele_name, prefix, local); + #( #( #tags )|* => { #read } )* + tag => { + strong_xml::log_skip_element!(#ele_name, tag); // skip the start tag reader.next(); - reader.read_to_end(prefix, local)?; + reader.read_to_end(tag)?; }, } } @@ -66,9 +53,8 @@ pub fn impl_read(element: Element) -> TokenStream { tag, name, fields, - prefix, namespaces, - } => named::read(&prefix, &tag, quote!(#name), &fields), + } => named::read(&tag, quote!(#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#name)), }, } diff --git a/strong-xml-derive/src/read/named.rs b/strong-xml-derive/src/read/named.rs index f86ff5d..fdbb585 100644 --- a/strong-xml-derive/src/read/named.rs +++ b/strong-xml-derive/src/read/named.rs @@ -2,11 +2,10 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{Ident, LitStr}; -use crate::types::{Field, Type}; +use crate::types::{Field, Type, QName}; pub fn read( - prefix: &Option, - local: &LitStr, + tag: &QName, ele_name: TokenStream, fields: &[Field], ) -> TokenStream { @@ -46,11 +45,10 @@ pub fn read( Field::Attribute { bind, ty, - prefix, tag, name, .. - } => Some(read_attrs(&prefix, &tag, &bind, &name, &ty, &ele_name)), + } => Some(read_attrs(&tag, &bind, &name, &ty, &ele_name)), _ => None, }); @@ -61,20 +59,7 @@ pub fn read( tags, name, .. - } => { - let tags: Vec<_> = tags.iter().map(|tag|{ - if tag.value().matches(":").count() > 1 { - panic!("child cannot have more than one colon in name.") - } - match tag.value().split_once(':') { - Some(("", local)) => (None, local.to_owned()), - Some((prefix, local)) => (Some(prefix.to_owned()), local.to_owned()), - None => (None, tag.value()) - } - }).collect(); - - Some(read_children(&tags[..], bind, name, ty, &ele_name)) - } + } => Some(read_children(&tags, bind, name, ty, &ele_name)), _ => None, }); @@ -82,17 +67,16 @@ pub fn read( Field::FlattenText { bind, ty, - prefix, tag, name, .. - } => Some(read_flatten_text(prefix, tag, bind, name, ty, &ele_name)), + } => Some(read_flatten_text(tag, bind, name, ty, &ele_name)), _ => None, }); let read_text_fields = fields.iter().filter_map(|field| match field { Field::Text { bind, ty, name, .. } => { - Some(read_text(&prefix, &local, bind, name, ty, &ele_name)) + Some(read_text(&tag, bind, name, ty, &ele_name)) } _ => None, }); @@ -111,12 +95,6 @@ pub fn read( return Ok(__res); }; - let prefix = if let Some(lit) = prefix { - quote!(#lit) - } else { - quote!("") - }; - let read_content = if is_text_element { quote! { #( #read_text_fields )* @@ -128,15 +106,15 @@ pub fn read( #return_fields } - while let Some((__prefix, __local)) = reader.find_element_start(Some((#prefix, #local)))? { - match (__prefix, __local) { + while let Some((__name)) = reader.find_element_start(Some(#tag))? { + match __name { #( #read_child_fields, )* #( #read_flatten_text_fields, )* - (prefix, local) => { - strong_xml::log_skip_element!(#ele_name, prefix, local); + tag => { + strong_xml::log_skip_element!(#ele_name, tag); // skip the start tag reader.next(); - reader.read_to_end(prefix, local)?; + reader.read_to_end(tag)?; }, } } @@ -150,13 +128,13 @@ pub fn read( #( #init_fields )* - reader.read_till_element_start(#prefix, #local)?; + reader.read_till_element_start(#tag)?; - while let Some((__prefix, __key, __value)) = reader.find_attribute()? { - match (__prefix, __key) { + while let Some((__key, __value)) = reader.find_attribute()? { + match __key { #( #read_attr_fields, )* - (prefix, key) => { - strong_xml::log_skip_attribute!(#ele_name, prefix, key); + key => { + strong_xml::log_skip_attribute!(#ele_name, key); }, } } @@ -195,8 +173,7 @@ fn return_value( } fn read_attrs( - prefix: &Option, - local: &LitStr, + tag: &QName, bind: &Ident, name: &TokenStream, ty: &Type, @@ -204,17 +181,11 @@ fn read_attrs( ) -> TokenStream { let from_str = from_str(ty); - let prefix = if let Some(lit) = prefix { - quote!(#lit) - } else { - quote!("") - }; - if ty.is_vec() { panic!("`attr` attribute doesn't support Vec."); } else { quote! { - (#prefix, #local) => { + #tag => { strong_xml::log_start_reading_field!(#ele_name, #name); #bind = Some(#from_str); @@ -226,8 +197,7 @@ fn read_attrs( } fn read_text( - prefix: &Option, - local: &LitStr, + tag: &QName, bind: &Ident, name: &TokenStream, ty: &Type, @@ -235,19 +205,13 @@ fn read_text( ) -> TokenStream { let from_str = from_str(ty); - let prefix = if let Some(lit) = prefix { - quote!(#lit) - } else { - quote!("") - }; - if ty.is_vec() { panic!("`text` attribute doesn't support Vec."); } else { quote! { strong_xml::log_start_reading_field!(#ele_name, #name); - let __value = reader.read_text(#prefix, #local)?; + let __value = reader.read_text(#tag)?; #bind = Some(#from_str); strong_xml::log_finish_reading_field!(#ele_name, #name); @@ -256,7 +220,7 @@ fn read_text( } fn read_children( - tags: &[(Option, String)], + tags: &[QName], bind: &Ident, name: &TokenStream, ty: &Type, @@ -272,14 +236,8 @@ fn read_children( _ => panic!("`child` attribute only supports Vec, Option and T."), }; - let (prefixes, locals): (Vec<_>, Vec<_>) = tags.iter().cloned().unzip(); - let prefixes = prefixes.iter().map(|prefix| if let Some(lit) = prefix { - quote!(#lit) - } else { - quote!("") - }); quote! { - #( (#prefixes, #locals) )|* => { + #( #tags )|* => { strong_xml::log_start_reading_field!(#ele_name, #name); #from_reader @@ -290,8 +248,7 @@ fn read_children( } fn read_flatten_text( - prefix: &Option, - local: &LitStr, + tag: &QName, bind: &Ident, name: &TokenStream, ty: &Type, @@ -299,26 +256,20 @@ fn read_flatten_text( ) -> TokenStream { let from_str = from_str(ty); - let prefix = if let Some(lit) = prefix { - quote!(#lit) - } else { - quote!("") - }; - let read_text = if ty.is_vec() { quote! { - let __value = reader.read_text(#prefix, #local)?; + let __value = reader.read_text(#tag)?; #bind.push(#from_str); } } else { quote! { - let __value = reader.read_text(#prefix, #local)?; + let __value = reader.read_text(#tag)?; #bind = Some(#from_str); } }; quote! { - (#prefix, #local) => { + #tag => { // skip element start reader.next(); diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index 18ec7af..dda18df 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,7 +1,7 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, fmt::Display}; -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use proc_macro2::{TokenStream, Span}; +use quote::{format_ident, quote, ToTokens}; use syn::{Lit::*, Meta::*, *}; use crate::utils::elide_type_lifetimes; @@ -32,10 +32,9 @@ pub enum Fields { /// } /// ``` Named { - tag: LitStr, + tag: QName, name: Ident, fields: Vec, - prefix: Option, namespaces: Namespaces, }, /// Newtype struct or newtype variant @@ -52,10 +51,9 @@ pub enum Fields { /// } /// ``` Newtype { - tags: Vec, + tags: Vec, name: Ident, ty: Type, - prefix: Option, namespaces: Namespaces, }, } @@ -73,9 +71,8 @@ pub enum Field { name: TokenStream, bind: Ident, ty: Type, - tag: LitStr, + tag: QName, default: bool, - prefix: Option, }, /// Child(ren) Field /// @@ -90,8 +87,7 @@ pub enum Field { bind: Ident, ty: Type, default: bool, - tags: Vec, - prefix: Option, + tags: Vec, }, /// Text Field /// @@ -120,8 +116,7 @@ pub enum Field { bind: Ident, ty: Type, default: bool, - prefix: Option, - tag: LitStr, + tag: QName, is_cdata: bool, }, } @@ -147,6 +142,12 @@ pub enum Type { OptionBool, } +#[derive(Clone)] +pub enum QName { + Prefixed(LitStr), + Unprefixed(LitStr) +} + impl Element { pub fn parse(input: DeriveInput) -> Element { match input.data { @@ -172,23 +173,14 @@ impl Fields { // Finding `tag` attribute let mut tags = Vec::new(); let mut namespaces: Namespaces = BTreeMap::default(); - let mut prefix = None; for meta in attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { NestedMeta::Meta(NameValue(m)) if m.path.is_ident("tag") => { if let Str(lit) = m.lit { - tags.push(lit); - } else { - panic!("Expected a string literal."); - } - } - NestedMeta::Meta(NameValue(m)) if m.path.is_ident("prefix") => { - if let Str(lit) = m.lit { - if prefix.is_some() { - panic!("Duplicate `ns` attribute."); - } else { - prefix = Some(lit); + match QName::parse(lit) { + Ok(q) => tags.push(q), + Err(e) => panic!("{}", e), } } else { panic!("Expected a string literal."); @@ -240,7 +232,6 @@ impl Fields { syn::Fields::Unit => Fields::Named { name, tag: tags.remove(0), - prefix, namespaces, fields: Vec::new(), }, @@ -254,7 +245,6 @@ impl Fields { name, tags, ty: Type::parse(field.ty), - prefix, namespaces, }; } @@ -264,7 +254,6 @@ impl Fields { name, tag: tags.remove(0), namespaces, - prefix, fields: fields .unnamed .into_iter() @@ -281,7 +270,6 @@ impl Fields { name, tag: tags.remove(0), namespaces, - prefix, fields: fields .into_iter() .map(|field| { @@ -303,7 +291,6 @@ impl Field { let mut is_text = false; let mut flatten_text_tag = None; let mut is_cdata = false; - let mut prefix = None; for meta in field.attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { @@ -327,7 +314,10 @@ impl Field { } else if flatten_text_tag.is_some() { panic!("`attr` attribute and `flatten_text` attribute is disjoint."); } else { - attr_tag = Some(lit); + match QName::parse(lit) { + Ok(q) => attr_tag = Some(q), + Err(e) => panic!("{}", e), + } } } else { panic!("Expected a string literal."); @@ -368,7 +358,10 @@ impl Field { } else if flatten_text_tag.is_some() { panic!("`child` attribute and `flatten_text` attribute is disjoint."); } else { - child_tags.push(lit); + match QName::parse(lit) { + Ok(q) => child_tags.push(q), + Err(e) => panic!("{}", e), + } } } else { panic!("Expected a string literal."); @@ -385,22 +378,10 @@ impl Field { } else if flatten_text_tag.is_some() { panic!("Duplicate `flatten_text` attribute."); } else { - flatten_text_tag = Some(lit); - } - } else { - panic!("Expected a string literal."); - } - } - NestedMeta::Meta(NameValue(m)) if m.path.is_ident("prefix") => { - if let Str(lit) = m.lit { - if is_text { - panic!("`prefix` attribute and `text` attribute is disjoint."); - } else if flatten_text_tag.is_some() { - panic!("`prefix` attribute and `flatten_text` attribute is disjoint."); - } else if prefix.is_some() { - panic!("Duplicate `prefix` attribute."); - } else { - prefix = Some(lit); + match QName::parse(lit) { + Ok(q) => flatten_text_tag = Some(q), + Err(e) => panic!("{}", e), + } } } else { panic!("Expected a string literal."); @@ -424,7 +405,6 @@ impl Field { bind, ty: Type::parse(field.ty), tag, - prefix, default, } } else if !child_tags.is_empty() { @@ -433,7 +413,6 @@ impl Field { bind, ty: Type::parse(field.ty), default, - prefix, tags: child_tags, } } else if is_text { @@ -449,7 +428,6 @@ impl Field { bind, ty: Type::parse(field.ty), default, - prefix, tag, is_cdata, } @@ -566,6 +544,70 @@ impl Type { } } } +use std::io::{Result, Error, ErrorKind}; + +impl QName { + pub fn parse(name: LitStr) -> Result { + let space_count = name.value().matches(' ').count(); + if space_count > 0 { + return Err(Error::new( + ErrorKind::Other, + "QName can not contain spaces", + )) + } + + let colon_count = name.value().matches(':').count(); + match colon_count { + 0 => Ok(QName::Unprefixed(name)), + 1 => Ok(QName::Prefixed(name)), + _ => Err(Error::new( + ErrorKind::Other, + "QName can only have a max of 1 colon", + )), + } + } + + pub fn prefix(&self) -> Option { + let name = self.to_string(); + if let Some((prefix, _)) = name.split_once(':') { + Some(prefix.to_owned()) + } else { + None + } + } + + pub fn local(&self) -> String { + let name = self.to_string(); + if let Some((_, local)) = name.split_once(':') { + local.to_owned() + } else { + name + } + } +} + +impl ToString for QName { + fn to_string(&self) -> String { + match self { + QName::Prefixed(tag) | + QName::Unprefixed(tag) => { + tag.value() + } + } + } +} + +impl ToTokens for QName { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + QName::Prefixed(tag) | + QName::Unprefixed(tag) => { + tag.to_tokens(tokens) + } + } + } +} + fn get_xml_meta(attr: Attribute) -> Option> { if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "xml" { diff --git a/strong-xml-derive/src/write/mod.rs b/strong-xml-derive/src/write/mod.rs index a6b90bf..e763010 100644 --- a/strong-xml-derive/src/write/mod.rs +++ b/strong-xml-derive/src/write/mod.rs @@ -30,11 +30,9 @@ pub fn impl_write(element: Element) -> TokenStream { tag, name, fields, - prefix, namespaces, } => named::write( tag, - prefix, quote!( #ele_name::#name ), &fields, &namespaces, @@ -57,7 +55,6 @@ pub fn impl_write(element: Element) -> TokenStream { tag, name, fields, - prefix, namespaces, } => { let bindings = fields.iter().map(|field| match field { @@ -67,7 +64,7 @@ pub fn impl_write(element: Element) -> TokenStream { | Field::FlattenText { bind, name, .. } => quote!( #name: #bind ), }); - let read = named::write(&tag, &prefix, quote!(#name), &fields, &namespaces); + let read = named::write(&tag, quote!(#name), &fields, &namespaces); quote! { let #ele_name { #( #bindings ),* } = self; diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index cf25429..5c53427 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -1,22 +1,22 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Ident, LitStr}; +use syn::Ident; -use crate::types::{Field, Type, Namespaces}; +use crate::types::{Field, Type, Namespaces, QName}; -pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, fields: &[Field], namespaces: &Namespaces) -> TokenStream { +pub fn write(tag: &QName, ele_name: TokenStream, fields: &[Field], namespaces: &Namespaces) -> TokenStream { let write_namespaces = write_namespaces(namespaces); let write_attributes = fields.iter().filter_map(|field| match field { - Field::Attribute { tag, prefix, bind, ty, .. } => Some(write_attrs(&tag, prefix, &bind, &ty, &ele_name)), + Field::Attribute { tag, bind, ty, .. } => Some(write_attrs(&tag, &bind, &ty, &ele_name)), _ => None, }); let write_text = fields.iter().filter_map(|field| match field { Field::Text { bind, ty, is_cdata,.. - } => Some(write_text(tag, prefix, bind, ty, &ele_name, *is_cdata)), + } => Some(write_text(tag, bind, ty, &ele_name, *is_cdata)), _ => None, }); @@ -27,7 +27,7 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field ty, is_cdata, .. - } => Some(write_flatten_text(tag, prefix, bind, ty, &ele_name, *is_cdata)), + } => Some(write_flatten_text(tag, bind, ty, &ele_name, *is_cdata)), _ => None, }); @@ -62,12 +62,6 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field _ => None, }); - let prefix = if let Some(prefix) = prefix { - quote!(Some(#prefix)) - } else { - quote!(None) - }; - let write_element_end = if is_leaf_element { quote! { writer.write_element_end_empty()?; } } else if is_text_element { @@ -80,7 +74,7 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field writer.write_element_end_open()?; #( #write_child )* #( #write_flatten_text )* - writer.write_element_end_close(#prefix, #tag)?; + writer.write_element_end_close(#tag)?; } } }; @@ -88,7 +82,7 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field quote! { strong_xml::log_start_writing!(#ele_name); - writer.write_element_start(#prefix, #tag)?; + writer.write_element_start(#tag)?; #write_namespaces #( #write_attributes )* @@ -101,15 +95,9 @@ pub fn write(tag: &LitStr, prefix: &Option, ele_name: TokenStream, field -fn write_attrs(tag: &LitStr, prefix: &Option, name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { +fn write_attrs(tag: &QName, name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { let to_str = to_str(ty); - let prefix = if let Some(prefix) = prefix { - quote!(Some(#prefix)) - } else { - quote!(None) - }; - if ty.is_vec() { panic!("`attr` attribute doesn't support Vec."); } else if ty.is_option() { @@ -117,7 +105,7 @@ fn write_attrs(tag: &LitStr, prefix: &Option, name: &Ident, ty: &Type, e strong_xml::log_start_writing_field!(#ele_name, #name); if let Some(__value) = #name { - writer.write_attribute(#prefix, #tag, #to_str)?; + writer.write_attribute(#tag, #to_str)?; } strong_xml::log_finish_writing_field!(#ele_name, #name); @@ -127,7 +115,7 @@ fn write_attrs(tag: &LitStr, prefix: &Option, name: &Ident, ty: &Type, e strong_xml::log_start_writing_field!(#ele_name, #name); let __value = #name; - writer.write_attribute(#prefix, #tag, #to_str)?; + writer.write_attribute(#tag, #to_str)?; strong_xml::log_finish_writing_field!(#ele_name, #name); } @@ -180,8 +168,7 @@ fn write_child(name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { } fn write_text( - tag: &LitStr, - prefix: &Option, + tag: &QName, name: &Ident, ty: &Type, ele_name: &TokenStream, @@ -194,12 +181,6 @@ fn write_text( quote!(write_text) }; - let prefix = if let Some(prefix) = prefix { - quote!(Some(#prefix)) - } else { - quote!(None) - }; - quote! { writer.write_element_end_open()?; @@ -211,13 +192,12 @@ fn write_text( strong_xml::log_finish_writing_field!(#ele_name, #name); - writer.write_element_end_close(#prefix, #tag)?; + writer.write_element_end_close(#tag)?; } } fn write_flatten_text( - tag: &LitStr, - prefix: &Option, + tag: &QName, name: &Ident, ty: &Type, ele_name: &TokenStream, @@ -225,18 +205,12 @@ fn write_flatten_text( ) -> TokenStream { let to_str = to_str(ty); - let prefix = if let Some(prefix) = prefix { - quote!(Some(#prefix)) - } else { - quote!(None) - }; - if ty.is_vec() { quote! { strong_xml::log_finish_writing_field!(#ele_name, #name); for __value in #name { - writer.write_flatten_text(#prefix, #tag, #to_str, #is_cdata)?; + writer.write_flatten_text(#tag, #to_str, #is_cdata)?; } strong_xml::log_finish_writing_field!(#ele_name, #name); @@ -246,7 +220,7 @@ fn write_flatten_text( strong_xml::log_finish_writing_field!(#ele_name, #name); if let Some(__value) = #name { - writer.write_flatten_text(#prefix, #tag, #to_str, #is_cdata)?; + writer.write_flatten_text(#tag, #to_str, #is_cdata)?; } strong_xml::log_finish_writing_field!(#ele_name, #name); @@ -256,7 +230,7 @@ fn write_flatten_text( strong_xml::log_finish_writing_field!(#ele_name, #name); let __value = &#name; - writer.write_flatten_text(#prefix, #tag, #to_str, #is_cdata)?; + writer.write_flatten_text(#tag, #to_str, #is_cdata)?; strong_xml::log_finish_writing_field!(#ele_name, #name); } diff --git a/strong-xml/src/log.rs b/strong-xml/src/log.rs index 801ba30..7ace48e 100644 --- a/strong-xml/src/log.rs +++ b/strong-xml/src/log.rs @@ -58,10 +58,10 @@ macro_rules! make_tag { #[macro_export] #[doc(hidden)] macro_rules! log_skip_attribute { - ($element:path, $prefix:ident, $local:ident) => { + ($element:path, $key:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip attribute `{}`"), - $crate::make_tag!($prefix, $local) + $key ); }; } @@ -69,10 +69,10 @@ macro_rules! log_skip_attribute { #[macro_export] #[doc(hidden)] macro_rules! log_skip_element { - ($element:path, $prefix:ident, $local:ident) => { + ($element:path, $name:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip element `{}`"), - $crate::make_tag!($prefix, $local) + $name ); }; } diff --git a/strong-xml/src/xml_reader.rs b/strong-xml/src/xml_reader.rs index 020b1fe..6a68d23 100644 --- a/strong-xml/src/xml_reader.rs +++ b/strong-xml/src/xml_reader.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::fmt::format; use std::iter::{Iterator, Peekable}; use xmlparser::ElementEnd; @@ -18,16 +17,6 @@ pub struct XmlReader<'a> { tokenizer: Peekable>, } -/// Xml Nma -/// -/// Holds both the namespace prefix and the local name -/// of a node or attribute -#[derive(PartialEq, Eq)] -pub struct XmlName<'a> { - pub prefix: &'a str, - pub local: &'a str, -} - impl<'a> XmlReader<'a> { #[inline] pub fn new(text: &'a str) -> XmlReader<'a> { @@ -58,8 +47,7 @@ impl<'a> XmlReader<'a> { #[inline] pub fn read_text( &mut self, - end_prefix: &'a str, - end_local: &'a str, + end_tag: &'a str, ) -> XmlResult> { let mut res = None; @@ -77,15 +65,15 @@ impl<'a> XmlReader<'a> { res = Some(Cow::Borrowed(text.as_str())); } Token::ElementEnd { - end: ElementEnd::Close(prefix, local), - .. + end: ElementEnd::Close(_, _), + span, } => { - if prefix.as_str() == end_prefix && local.as_str() == end_local { + if end_tag == &span[2..span.len()-1] { break; } else { return Err(XmlError::TagMismatch { - expected: Self::make_name(end_prefix, end_local).to_string(), - found: Self::make_name(prefix.as_str(), local.as_str()).to_string(), + expected: end_tag.to_owned(), + found: span[2..span.len()-1].to_owned() }); } } @@ -101,14 +89,15 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn read_till_element_start(&mut self, end_prefix: &str, end_local: &str) -> XmlResult<()> { + pub fn read_till_element_start(&mut self, end_tag: &str) -> XmlResult<()> { while let Some(token) = self.next() { match token? { - Token::ElementStart { prefix, local, .. } => { - if prefix.as_str() == end_prefix && local.as_str() == end_local { + Token::ElementStart { span, .. } => { + let tag = &span.as_str()[1..]; + if end_tag == tag { break; } else { - self.read_to_end(prefix.as_str(), local.as_str())?; + self.read_to_end(tag)?; } } Token::ElementEnd { .. } @@ -126,20 +115,24 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn find_attribute(&mut self) -> XmlResult)>> { + pub fn find_attribute(&mut self) -> XmlResult)>> { if let Some(token) = self.tokenizer.peek() { match token { Ok(Token::Attribute { prefix, local, value, - .. + span, }) => { - let prefix = prefix.as_str(); - let local = local.as_str(); + let length = local.len() + if !prefix.is_empty() { + prefix.len() + 1 + } else { + 0 + }; + let name = &span.as_str()[0..length]; let value = Cow::Borrowed(value.as_str()); self.next(); - return Ok(Some((prefix, local, value))); + return Ok(Some((name, value))); } Ok(Token::ElementEnd { end: ElementEnd::Open, @@ -167,25 +160,28 @@ impl<'a> XmlReader<'a> { #[inline] pub fn find_element_start( &mut self, - end_tag: Option<(&str, &str)>, - ) -> XmlResult> { + end_tag: Option<&str>, + ) -> XmlResult> { while let Some(token) = self.tokenizer.peek() { match token { - Ok(Token::ElementStart { prefix, local, .. }) => { - return Ok(Some((prefix.as_str(), local.as_str()))); + Ok(Token::ElementStart { span, .. }) => { + let name = &span.as_str()[1..]; + return Ok(Some(name)); } Ok(Token::ElementEnd { - end: ElementEnd::Close(prefix, local), - .. + end: ElementEnd::Close(_, _), + span }) if end_tag.is_some() => { - let (end_prefix, end_local) = end_tag.unwrap(); - if prefix.as_str() == end_prefix && local.as_str() == end_local { + let end_tag = end_tag.unwrap(); + + let name = &span.as_str()[2..span.len()-1]; + if end_tag == name { self.next(); return Ok(None); } else { return Err(XmlError::TagMismatch { - expected: Self::make_name(end_prefix, end_local).to_string(), - found: Self::make_name(prefix.as_str(), local.as_str()).to_string(), + expected: end_tag.to_string(), + found: name.to_string(), }); } } @@ -204,7 +200,7 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn read_to_end(&mut self, end_prefix: &str, end_local: &str) -> XmlResult<()> { + pub fn read_to_end(&mut self, end_tag: &str) -> XmlResult<()> { while let Some(token) = self.next() { match token? { // if this element is emtpy, just return @@ -230,9 +226,8 @@ impl<'a> XmlReader<'a> { while let Some(token) = self.next() { match token? { - Token::ElementStart { prefix, local, .. } - if prefix.as_str() == end_prefix && local.as_str() == end_local => - { + Token::ElementStart { span, .. } if end_tag == &span.as_str()[1..] => { + let name = &span.as_str()[1..]; while let Some(token) = self.next() { match token? { Token::ElementEnd { @@ -264,9 +259,9 @@ impl<'a> XmlReader<'a> { } } Token::ElementEnd { - end: ElementEnd::Close(prefix, local), - .. - } if prefix.as_str() == end_prefix && local.as_str() == end_local => { + end: ElementEnd::Close(_, _), + span, + } if end_tag == &span[2..span.len()-1] => { depth -= 1; if depth == 0 { return Ok(()); @@ -280,79 +275,61 @@ impl<'a> XmlReader<'a> { } } -impl<'a> XmlName<'a> { - #[inline] - pub fn new(prefix: &'a str, local: &'a str) -> XmlName<'a> { - XmlName { prefix, local } - } - - #[inline] - pub fn cmp_raw

(&self, prefix: P, local: &'a str) -> bool - where - P: Into>, - { - // this make the method able to be called like - // cmp_raw(None, "local") or cmp_raw("prefix", "local") - let prefix: Option<_> = prefix.into(); - (prefix == None && self.prefix == "") || prefix == Some(self.prefix) && local == self.local - } -} - #[test] fn read_text() -> XmlResult<()> { let mut reader = XmlReader::new(""); assert!(reader.next().is_some()); // "text"); assert!(reader.next().is_some()); // "text"); assert!(reader.next().is_some()); // ""'<>&"); assert!(reader.next().is_some()); // "&"#); + assert_eq!(reader.read_text("parent")?, r#""'<>&"#); assert!(reader.next().is_none()); let mut reader = XmlReader::new(""); assert!(reader.next().is_some()); // ""); assert!(reader.next().is_some()); // ""); assert!(reader.next().is_some()); // "]]>"); assert!(reader.next().is_some()); // ""); + assert_eq!(reader.read_text("parent")?, ""); assert!(reader.next().is_none()); reader = XmlReader::new(""); assert!(reader.next().is_some()); // " XmlResult<()> { fn read_till_element_start() -> XmlResult<()> { let mut reader = XmlReader::new(""); - reader.read_till_element_start("", "tag")?; + reader.read_till_element_start("tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_none()); - + reader = XmlReader::new(""); assert!(reader.next().is_some()); // "" - reader.read_till_element_start("", "tag")?; + reader.read_till_element_start("tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); + println!("HERE"); reader = XmlReader::new(""); assert!(reader.next().is_some()); // "" - reader.read_till_element_start("", "tag")?; + reader.read_till_element_start("tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -388,7 +366,7 @@ fn read_till_element_start() -> XmlResult<()> { assert!(reader.next().is_some()); // "" - reader.read_till_element_start("", "tag")?; + reader.read_till_element_start("tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -397,7 +375,7 @@ fn read_till_element_start() -> XmlResult<()> { assert!(reader.next().is_some()); // "" - reader.read_till_element_start("", "tag")?; + reader.read_till_element_start("tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -412,7 +390,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -421,7 +399,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -430,7 +408,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); @@ -439,7 +417,7 @@ fn read_to_end() -> XmlResult<()> { assert!(reader.next().is_some()); // "" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index 5cc1523..5a549e8 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -24,13 +24,8 @@ impl XmlWriter { self.inner } - pub fn write_element_start(&mut self, prefix: Option<&str>, tag: &str) -> Result<()> { - self.set_prefixes.push(BTreeSet::new()); - if let Some(prefix) = prefix { - write!(self.inner, "<{}:{}", prefix, tag) - } else { - write!(self.inner, "<{}", tag) - } + pub fn write_element_start(&mut self, tag: &str) -> Result<()> { + write!(self.inner, "<{}", tag) } pub fn write_namespace_declaration( @@ -39,7 +34,7 @@ impl XmlWriter { ns: &'static str, ) -> Result<()> { if !self.is_prefix_defined_as(&prefix, ns) { - self.push_changed_namespace(prefix, ns)?; + //self.push_changed_namespace(prefix, ns)?; if let Some(prefix) = prefix { write!(self.inner, r#" xmlns:{}="{}""#, prefix, xml_escape(ns)) } else { @@ -50,12 +45,8 @@ impl XmlWriter { } } - pub fn write_attribute(&mut self, prefix: Option<&str>, key: &str, value: &str) -> Result<()> { - if let Some(prefix) = prefix { - write!(self.inner, r#" {}:{}="{}""#, prefix, key, xml_escape(value)) - } else { - write!(self.inner, r#" {}="{}""#, key, xml_escape(value)) - } + pub fn write_attribute(&mut self, key: &str, value: &str) -> Result<()> { + write!(self.inner, r#" {}="{}""#, key, xml_escape(value)) } pub fn write_text(&mut self, content: &str) -> Result<()> { @@ -72,33 +63,28 @@ impl XmlWriter { pub fn write_flatten_text( &mut self, - prefix: Option<&str>, tag: &str, content: &str, is_cdata: bool, ) -> Result<()> { - self.write_element_start(prefix, tag)?; + self.write_element_start(tag)?; self.write_element_end_open()?; if is_cdata { self.write_cdata_text(content)?; } else { self.write_text(content)?; } - self.write_element_end_close(prefix, tag)?; + self.write_element_end_close(tag)?; Ok(()) } - pub fn write_element_end_close(&mut self, prefix: Option<&str>, tag: &str) -> Result<()> { - self.pop_changed_namespaces()?; - if let Some(prefix) = prefix { - write!(self.inner, "", prefix, tag) - } else { - write!(self.inner, "", tag) - } + pub fn write_element_end_close(&mut self, tag: &str) -> Result<()> { + //self.pop_changed_namespaces()?; + write!(self.inner, "", tag) } pub fn write_element_end_empty(&mut self) -> Result<()> { - self.pop_changed_namespaces()?; + //self.pop_changed_namespaces()?; write!(self.inner, "/>") } diff --git a/test-suite/tests/bool.rs b/test-suite/tests/bool.rs index 67c5513..172b909 100644 --- a/test-suite/tests/bool.rs +++ b/test-suite/tests/bool.rs @@ -7,6 +7,7 @@ struct Attr { attr: Option, } + #[derive(XmlRead, XmlWrite, PartialEq, Debug)] #[xml(tag = "text")] struct FlattenText { diff --git a/test-suite/tests/enum.rs b/test-suite/tests/enum.rs index e7f931a..0252b91 100644 --- a/test-suite/tests/enum.rs +++ b/test-suite/tests/enum.rs @@ -23,6 +23,8 @@ enum AB { #[xml(tag = "b")] B(B), } +// Recursive expansion of XmlRead! macro +// ====================================== #[derive(XmlWrite, XmlRead, PartialEq, Debug)] enum CD { @@ -32,6 +34,7 @@ enum CD { D(D), } + #[derive(XmlWrite, XmlRead, PartialEq, Debug)] enum ABCDEFG { #[xml(tag = "a", tag = "b")] @@ -54,6 +57,8 @@ enum ABCDEFG { }, } + + #[test] fn test() -> XmlResult<()> { let _ = env_logger::builder() From 3f7fb7bece90bd0914be3d98366af9bcfae15cf8 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Thu, 12 May 2022 22:09:45 -0500 Subject: [PATCH 11/15] Start of rewrite format --- strong-xml-derive/src/read/mod.rs | 11 ++--- strong-xml-derive/src/read/named.rs | 14 ++---- strong-xml-derive/src/types.rs | 68 +++++++++++++------------ strong-xml-derive/src/write/mod.rs | 7 +-- strong-xml-derive/src/write/named.rs | 45 ++++++++++------- strong-xml/src/log.rs | 1 - strong-xml/src/xml_reader.rs | 46 ++++++----------- strong-xml/src/xml_writer.rs | 7 +-- test-suite/tests/bool.rs | 1 - test-suite/tests/enum.rs | 3 -- test-suite/tests/namespaces.rs | 74 ++++++++++++++-------------- 11 files changed, 125 insertions(+), 152 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index ab1ef86..2cd686a 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -13,13 +13,10 @@ pub fn impl_read(element: Element) -> TokenStream { name: ele_name, variants, } => { - - let tags = variants - .iter() - .map(|variant| match variant { - Fields::Newtype { tags, .. } => tags.iter().cloned().collect(), - Fields::Named { tag, .. } => vec!(tag.clone()) - }); + let tags = variants.iter().map(|variant| match variant { + Fields::Newtype { tags, .. } => tags.iter().cloned().collect(), + Fields::Named { tag, .. } => vec![tag.clone()], + }); let read = variants.iter().map(|variant| match variant { Fields::Named { diff --git a/strong-xml-derive/src/read/named.rs b/strong-xml-derive/src/read/named.rs index fdbb585..9eb83c4 100644 --- a/strong-xml-derive/src/read/named.rs +++ b/strong-xml-derive/src/read/named.rs @@ -2,13 +2,9 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{Ident, LitStr}; -use crate::types::{Field, Type, QName}; +use crate::types::{Field, QName, Type}; -pub fn read( - tag: &QName, - ele_name: TokenStream, - fields: &[Field], -) -> TokenStream { +pub fn read(tag: &QName, ele_name: TokenStream, fields: &[Field]) -> TokenStream { let init_fields = fields.iter().map(|field| match field { Field::Attribute { bind, ty, .. } | Field::Child { bind, ty, .. } @@ -75,9 +71,7 @@ pub fn read( }); let read_text_fields = fields.iter().filter_map(|field| match field { - Field::Text { bind, ty, name, .. } => { - Some(read_text(&tag, bind, name, ty, &ele_name)) - } + Field::Text { bind, ty, name, .. } => Some(read_text(&tag, bind, name, ty, &ele_name)), _ => None, }); @@ -204,7 +198,7 @@ fn read_text( ele_name: &TokenStream, ) -> TokenStream { let from_str = from_str(ty); - + if ty.is_vec() { panic!("`text` attribute doesn't support Vec."); } else { diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index dda18df..1480bc8 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,13 +1,11 @@ use std::{collections::BTreeMap, fmt::Display}; -use proc_macro2::{TokenStream, Span}; +use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::{Lit::*, Meta::*, *}; use crate::utils::elide_type_lifetimes; -pub type Namespaces = BTreeMap, String>; - pub enum Element { Struct { name: Ident, fields: Fields }, Enum { name: Ident, variants: Vec }, @@ -35,7 +33,7 @@ pub enum Fields { tag: QName, name: Ident, fields: Vec, - namespaces: Namespaces, + namespaces: NamespaceDefs, }, /// Newtype struct or newtype variant /// @@ -54,7 +52,7 @@ pub enum Fields { tags: Vec, name: Ident, ty: Type, - namespaces: Namespaces, + namespaces: NamespaceDefs, }, } @@ -88,6 +86,7 @@ pub enum Field { ty: Type, default: bool, tags: Vec, + namespaces: NamespaceDefs, }, /// Text Field /// @@ -145,9 +144,16 @@ pub enum Type { #[derive(Clone)] pub enum QName { Prefixed(LitStr), - Unprefixed(LitStr) + Unprefixed(LitStr), +} + +pub struct NamespaceDef { + prefix: Option, + namespace: String, } +pub type NamespaceDefs = BTreeMap, NamespaceDef>; + impl Element { pub fn parse(input: DeriveInput) -> Element { match input.data { @@ -172,7 +178,7 @@ impl Fields { pub fn parse(fields: syn::Fields, attrs: Vec, name: Ident) -> Fields { // Finding `tag` attribute let mut tags = Vec::new(); - let mut namespaces: Namespaces = BTreeMap::default(); + let mut namespaces: NamespaceDefs = BTreeMap::default(); for meta in attrs.into_iter().filter_map(get_xml_meta).flatten() { match meta { @@ -218,7 +224,7 @@ impl Fields { panic!("prefix cannot start with `xml`"); } } - namespaces.insert(prefix, namespace); + namespaces.insert(prefix.clone(), NamespaceDef { prefix, namespace }); } _ => (), } @@ -414,6 +420,7 @@ impl Field { ty: Type::parse(field.ty), default, tags: child_tags, + namespaces: NamespaceDefs::new(), } } else if is_text { Field::Text { @@ -544,16 +551,13 @@ impl Type { } } } -use std::io::{Result, Error, ErrorKind}; +use std::io::{Error, ErrorKind, Result}; impl QName { pub fn parse(name: LitStr) -> Result { let space_count = name.value().matches(' ').count(); if space_count > 0 { - return Err(Error::new( - ErrorKind::Other, - "QName can not contain spaces", - )) + return Err(Error::new(ErrorKind::Other, "QName can not contain spaces")); } let colon_count = name.value().matches(':').count(); @@ -568,20 +572,19 @@ impl QName { } pub fn prefix(&self) -> Option { - let name = self.to_string(); - if let Some((prefix, _)) = name.split_once(':') { - Some(prefix.to_owned()) - } else { - None + match self { + Self::Prefixed(name) => name + .value() + .split_once(":") + .map(|(prefix, _)| prefix.to_owned()), + Self::Unprefixed(_) => None, } } pub fn local(&self) -> String { - let name = self.to_string(); - if let Some((_, local)) = name.split_once(':') { - local.to_owned() - } else { - name + match self { + Self::Prefixed(name) => name.value().split_once(":").unwrap().1.to_owned(), + Self::Unprefixed(name) => name.value(), } } } @@ -589,10 +592,7 @@ impl QName { impl ToString for QName { fn to_string(&self) -> String { match self { - QName::Prefixed(tag) | - QName::Unprefixed(tag) => { - tag.value() - } + QName::Prefixed(tag) | QName::Unprefixed(tag) => tag.value(), } } } @@ -600,14 +600,20 @@ impl ToString for QName { impl ToTokens for QName { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - QName::Prefixed(tag) | - QName::Unprefixed(tag) => { - tag.to_tokens(tokens) - } + QName::Prefixed(tag) | QName::Unprefixed(tag) => tag.to_tokens(tokens), } } } +impl NamespaceDef { + pub fn prefix(&self) -> Option<&str> { + self.prefix.as_deref() + } + + pub fn namespace(&self) -> &str { + &self.namespace + } +} fn get_xml_meta(attr: Attribute) -> Option> { if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "xml" { diff --git a/strong-xml-derive/src/write/mod.rs b/strong-xml-derive/src/write/mod.rs index e763010..b4df629 100644 --- a/strong-xml-derive/src/write/mod.rs +++ b/strong-xml-derive/src/write/mod.rs @@ -31,12 +31,7 @@ pub fn impl_write(element: Element) -> TokenStream { name, fields, namespaces, - } => named::write( - tag, - quote!( #ele_name::#name ), - &fields, - &namespaces, - ), + } => named::write(tag, quote!( #ele_name::#name ), &fields, &namespaces), Fields::Newtype { name, .. } => newtype::write(quote!( #ele_name::#name )), }); diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index 5c53427..fd46188 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -2,10 +2,14 @@ use proc_macro2::TokenStream; use quote::quote; use syn::Ident; -use crate::types::{Field, Type, Namespaces, QName}; +use crate::types::{Field, NamespaceDef, NamespaceDefs, QName, Type}; -pub fn write(tag: &QName, ele_name: TokenStream, fields: &[Field], namespaces: &Namespaces) -> TokenStream { - +pub fn write( + tag: &QName, + ele_name: TokenStream, + fields: &[Field], + namespaces: &NamespaceDefs, +) -> TokenStream { let write_namespaces = write_namespaces(namespaces); let write_attributes = fields.iter().filter_map(|field| match field { @@ -15,7 +19,7 @@ pub fn write(tag: &QName, ele_name: TokenStream, fields: &[Field], namespaces: & let write_text = fields.iter().filter_map(|field| match field { Field::Text { - bind, ty, is_cdata,.. + bind, ty, is_cdata, .. } => Some(write_text(tag, bind, ty, &ele_name, *is_cdata)), _ => None, }); @@ -83,8 +87,8 @@ pub fn write(tag: &QName, ele_name: TokenStream, fields: &[Field], namespaces: & strong_xml::log_start_writing!(#ele_name); writer.write_element_start(#tag)?; - - #write_namespaces + + #write_namespaces #( #write_attributes )* #write_element_end @@ -93,8 +97,6 @@ pub fn write(tag: &QName, ele_name: TokenStream, fields: &[Field], namespaces: & } } - - fn write_attrs(tag: &QName, name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { let to_str = to_str(ty); @@ -122,18 +124,23 @@ fn write_attrs(tag: &QName, name: &Ident, ty: &Type, ele_name: &TokenStream) -> } } +fn write_namespaces(namespaces: &NamespaceDefs) -> TokenStream { + namespaces + .values() + .map(|namespace_def| { + let namespace = namespace_def.prefix(); -fn write_namespaces(namespaces: &Namespaces) -> TokenStream { - namespaces.iter().map(|(prefix, ns)|{ - let prefix = if let Some(prefix) = prefix { - quote!(Some(#prefix)) - } else { - quote!(None) - }; - quote! { - writer.write_namespace_declaration(#prefix, #ns)?; - } - }).collect() + let prefix = if let Some(prefix) = namespace_def.prefix() { + quote!(Some(#prefix)) + } else { + quote!(None) + }; + + quote! { + writer.write_namespace_declaration(#prefix, #namespace)?; + } + }) + .collect() } fn write_child(name: &Ident, ty: &Type, ele_name: &TokenStream) -> TokenStream { diff --git a/strong-xml/src/log.rs b/strong-xml/src/log.rs index 7ace48e..cd82b97 100644 --- a/strong-xml/src/log.rs +++ b/strong-xml/src/log.rs @@ -42,7 +42,6 @@ macro_rules! log_finish_reading_field { }; } - #[macro_export] #[doc(hidden)] macro_rules! make_tag { diff --git a/strong-xml/src/xml_reader.rs b/strong-xml/src/xml_reader.rs index 6a68d23..291a7ce 100644 --- a/strong-xml/src/xml_reader.rs +++ b/strong-xml/src/xml_reader.rs @@ -36,19 +36,7 @@ impl<'a> XmlReader<'a> { } #[inline] - fn make_name<'r>(prefix: &'r str, local: &'r str) -> Cow<'r, str> { - if prefix.is_empty() { - Cow::Borrowed(local) - } else { - Cow::Owned(prefix.to_owned() + ":" + local) - } - } - - #[inline] - pub fn read_text( - &mut self, - end_tag: &'a str, - ) -> XmlResult> { + pub fn read_text(&mut self, end_tag: &'a str) -> XmlResult> { let mut res = None; while let Some(token) = self.next() { @@ -68,12 +56,12 @@ impl<'a> XmlReader<'a> { end: ElementEnd::Close(_, _), span, } => { - if end_tag == &span[2..span.len()-1] { + if end_tag == &span[2..span.len() - 1] { break; } else { return Err(XmlError::TagMismatch { expected: end_tag.to_owned(), - found: span[2..span.len()-1].to_owned() + found: span[2..span.len() - 1].to_owned(), }); } } @@ -124,11 +112,12 @@ impl<'a> XmlReader<'a> { value, span, }) => { - let length = local.len() + if !prefix.is_empty() { - prefix.len() + 1 - } else { - 0 - }; + let length = local.len() + + if !prefix.is_empty() { + prefix.len() + 1 + } else { + 0 + }; let name = &span.as_str()[0..length]; let value = Cow::Borrowed(value.as_str()); self.next(); @@ -158,10 +147,7 @@ impl<'a> XmlReader<'a> { } #[inline] - pub fn find_element_start( - &mut self, - end_tag: Option<&str>, - ) -> XmlResult> { + pub fn find_element_start(&mut self, end_tag: Option<&str>) -> XmlResult> { while let Some(token) = self.tokenizer.peek() { match token { Ok(Token::ElementStart { span, .. }) => { @@ -170,11 +156,11 @@ impl<'a> XmlReader<'a> { } Ok(Token::ElementEnd { end: ElementEnd::Close(_, _), - span + span, }) if end_tag.is_some() => { let end_tag = end_tag.unwrap(); - let name = &span.as_str()[2..span.len()-1]; + let name = &span.as_str()[2..span.len() - 1]; if end_tag == name { self.next(); return Ok(None); @@ -227,7 +213,6 @@ impl<'a> XmlReader<'a> { while let Some(token) = self.next() { match token? { Token::ElementStart { span, .. } if end_tag == &span.as_str()[1..] => { - let name = &span.as_str()[1..]; while let Some(token) = self.next() { match token? { Token::ElementEnd { @@ -261,7 +246,7 @@ impl<'a> XmlReader<'a> { Token::ElementEnd { end: ElementEnd::Close(_, _), span, - } if end_tag == &span[2..span.len()-1] => { + } if end_tag == &span[2..span.len() - 1] => { depth -= 1; if depth == 0 { return Ok(()); @@ -294,7 +279,7 @@ fn read_text() -> XmlResult<()> { assert!(reader.next().is_some()); // ""'<>&"); assert!(reader.next().is_some()); // " XmlResult<()> { reader.read_till_element_start("tag")?; assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_none()); - + reader = XmlReader::new(""); assert!(reader.next().is_some()); // " XmlResult<()> { assert!(reader.next().is_some()); // "/>" assert!(reader.next().is_some()); // "" assert!(reader.next().is_none()); - println!("HERE"); reader = XmlReader::new(""); diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index 5a549e8..e0323de 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -61,12 +61,7 @@ impl XmlWriter { write!(self.inner, ">") } - pub fn write_flatten_text( - &mut self, - tag: &str, - content: &str, - is_cdata: bool, - ) -> Result<()> { + pub fn write_flatten_text(&mut self, tag: &str, content: &str, is_cdata: bool) -> Result<()> { self.write_element_start(tag)?; self.write_element_end_open()?; if is_cdata { diff --git a/test-suite/tests/bool.rs b/test-suite/tests/bool.rs index 172b909..67c5513 100644 --- a/test-suite/tests/bool.rs +++ b/test-suite/tests/bool.rs @@ -7,7 +7,6 @@ struct Attr { attr: Option, } - #[derive(XmlRead, XmlWrite, PartialEq, Debug)] #[xml(tag = "text")] struct FlattenText { diff --git a/test-suite/tests/enum.rs b/test-suite/tests/enum.rs index 0252b91..c6fb646 100644 --- a/test-suite/tests/enum.rs +++ b/test-suite/tests/enum.rs @@ -34,7 +34,6 @@ enum CD { D(D), } - #[derive(XmlWrite, XmlRead, PartialEq, Debug)] enum ABCDEFG { #[xml(tag = "a", tag = "b")] @@ -57,8 +56,6 @@ enum ABCDEFG { }, } - - #[test] fn test() -> XmlResult<()> { let _ = env_logger::builder() diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index 01f79be..1957af1 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -2,7 +2,6 @@ use strong_xml::{XmlRead, XmlResult, XmlWrite}; #[test] fn test() -> XmlResult<()> { - #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com")] struct Nested { @@ -10,12 +9,11 @@ fn test() -> XmlResult<()> { contents: Vec, } - let _ = env_logger::builder() .is_test(true) .format_timestamp(None) .try_init(); - + assert_eq!( (Nested { contents: vec![Nested { @@ -27,48 +25,50 @@ fn test() -> XmlResult<()> { .to_string()?, r#""# ); - + Ok(()) } - - #[test] fn test2() -> XmlResult<()> { - #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com", ns = "n2: http://www.example.com")] + #[xml( + tag = "nested", + prefix = "n", + ns = "n: http://www.example.com", + ns = "n2: http://www.example.com" + )] struct Nested { - #[xml(child="nested", prefix = "n")] + #[xml(child = "nested", prefix = "n")] nested: Vec, - #[xml(child="nested2", prefix = "n2")] - nested2: Nested2 + #[xml(child = "nested2", prefix = "n2")] + nested2: Nested2, } #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "nested2", prefix = "n2", ns = "n2: http://www.example.com")] + #[xml(tag = "nested2", prefix = "n2", ns = "n2: http://www.example.com")] struct Nested2 { - #[xml(attr="nest", prefix = "n2")] - value: String + #[xml(attr = "nest", prefix = "n2")] + value: String, } - let _ = env_logger::builder() .is_test(true) .format_timestamp(None) .try_init(); - + assert_eq!( (Nested { nested: vec![], - nested2: Nested2 { value: "hello world".into() } + nested2: Nested2 { + value: "hello world".into() + } }) .to_string()?, r#""# ); - - /* + /* assert_eq!( (Nested { nested: vec![], @@ -78,42 +78,42 @@ fn test2() -> XmlResult<()> { ); */ - - - - #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "tag", prefix = "a", ns = "a: http://www.example.com")] + #[xml(tag = "tag", prefix = "a", ns = "a: http://www.example.com")] struct A { #[xml(text)] - value: String + value: String, } - #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "tag", prefix = "b", ns = "b: http://www.example.com/1")] + #[xml(tag = "tag", prefix = "b", ns = "b: http://www.example.com/1")] struct B { #[xml(text)] - value: String + value: String, } - #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml(tag = "root", prefix = "a", ns = "a: ns_a", ns = "b: ns_b")] struct C { - #[xml(child="tag", prefix = "a")] + #[xml(child = "tag", prefix = "a")] a: A, - #[xml(child="tag", prefix = "b")] - b: B + #[xml(child = "tag", prefix = "b")] + b: B, } assert_eq!( - C{a:A{value: "hello".into()}, b: B{value: "world".into()}}, - C::from_str(r#"helloworld"#)? + C { + a: A { + value: "hello".into() + }, + b: B { + value: "world".into() + } + }, + C::from_str( + r#"helloworld"# + )? ); - - - Ok(()) } From 07df6a231b90dd09387f794d2dda39d05ae08550 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Fri, 13 May 2022 14:41:28 -0500 Subject: [PATCH 12/15] wip: work on namespaces, reverted unneeded changes --- strong-xml-derive/src/read/mod.rs | 3 +-- strong-xml-derive/src/read/named.rs | 8 +++---- strong-xml-derive/src/types.rs | 5 ++-- strong-xml-derive/src/write/named.rs | 2 +- strong-xml/src/log.rs | 16 ++----------- strong-xml/src/xml_reader.rs | 11 ++++----- strong-xml/src/xml_writer.rs | 10 ++++---- test-suite/tests/enum.rs | 2 -- test-suite/tests/namespaces.rs | 36 +++++++++++++++++----------- 9 files changed, 42 insertions(+), 51 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index 2cd686a..433c8c7 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -5,7 +5,6 @@ use crate::types::{Element, Fields}; use proc_macro2::TokenStream; use quote::quote; -use syn::LitStr; pub fn impl_read(element: Element) -> TokenStream { match element { @@ -14,7 +13,7 @@ pub fn impl_read(element: Element) -> TokenStream { variants, } => { let tags = variants.iter().map(|variant| match variant { - Fields::Newtype { tags, .. } => tags.iter().cloned().collect(), + Fields::Newtype { tags, .. } => tags.clone(), Fields::Named { tag, .. } => vec![tag.clone()], }); diff --git a/strong-xml-derive/src/read/named.rs b/strong-xml-derive/src/read/named.rs index 9eb83c4..9d34556 100644 --- a/strong-xml-derive/src/read/named.rs +++ b/strong-xml-derive/src/read/named.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Ident, LitStr}; +use syn::Ident; use crate::types::{Field, QName, Type}; @@ -55,7 +55,7 @@ pub fn read(tag: &QName, ele_name: TokenStream, fields: &[Field]) -> TokenStream tags, name, .. - } => Some(read_children(&tags, bind, name, ty, &ele_name)), + } => Some(read_children(tags, bind, name, ty, &ele_name)), _ => None, }); @@ -100,8 +100,8 @@ pub fn read(tag: &QName, ele_name: TokenStream, fields: &[Field]) -> TokenStream #return_fields } - while let Some((__name)) = reader.find_element_start(Some(#tag))? { - match __name { + while let Some(__tag) = reader.find_element_start(Some(#tag))? { + match __tag { #( #read_child_fields, )* #( #read_flatten_text_fields, )* tag => { diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index 1480bc8..fa8ce62 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,6 +1,5 @@ -use std::{collections::BTreeMap, fmt::Display}; - -use proc_macro2::{Span, TokenStream}; +use std::collections::BTreeMap; +use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; use syn::{Lit::*, Meta::*, *}; diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index fd46188..1d35529 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -128,7 +128,7 @@ fn write_namespaces(namespaces: &NamespaceDefs) -> TokenStream { namespaces .values() .map(|namespace_def| { - let namespace = namespace_def.prefix(); + let namespace = namespace_def.namespace(); let prefix = if let Some(prefix) = namespace_def.prefix() { quote!(Some(#prefix)) diff --git a/strong-xml/src/log.rs b/strong-xml/src/log.rs index cd82b97..c394429 100644 --- a/strong-xml/src/log.rs +++ b/strong-xml/src/log.rs @@ -42,18 +42,6 @@ macro_rules! log_finish_reading_field { }; } -#[macro_export] -#[doc(hidden)] -macro_rules! make_tag { - ($prefix:ident, $local:ident) => { - if $prefix.is_empty() { - $local.to_owned() - } else { - ($prefix.to_owned() + ":" + $local) - } - }; -} - #[macro_export] #[doc(hidden)] macro_rules! log_skip_attribute { @@ -68,10 +56,10 @@ macro_rules! log_skip_attribute { #[macro_export] #[doc(hidden)] macro_rules! log_skip_element { - ($element:path, $name:ident) => { + ($element:path, $tag:ident) => { $crate::lib::log::info!( concat!("[", stringify!($element), "] Skip element `{}`"), - $name + $tag ); }; } diff --git a/strong-xml/src/xml_reader.rs b/strong-xml/src/xml_reader.rs index 291a7ce..62d17c4 100644 --- a/strong-xml/src/xml_reader.rs +++ b/strong-xml/src/xml_reader.rs @@ -151,23 +151,21 @@ impl<'a> XmlReader<'a> { while let Some(token) = self.tokenizer.peek() { match token { Ok(Token::ElementStart { span, .. }) => { - let name = &span.as_str()[1..]; - return Ok(Some(name)); + return Ok(Some(&span.as_str()[1..])); } Ok(Token::ElementEnd { end: ElementEnd::Close(_, _), span, }) if end_tag.is_some() => { let end_tag = end_tag.unwrap(); - - let name = &span.as_str()[2..span.len() - 1]; - if end_tag == name { + let tag = &span[2..span.len() - 1];// remove `` + if tag == end_tag { self.next(); return Ok(None); } else { return Err(XmlError::TagMismatch { expected: end_tag.to_string(), - found: name.to_string(), + found: tag.to_string(), }); } } @@ -182,6 +180,7 @@ impl<'a> XmlReader<'a> { } } } + Err(XmlError::UnexpectedEof) } diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index e0323de..33a3768 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -1,5 +1,4 @@ -use std::collections::BTreeMap; -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use std::io::Result; use std::io::Write; @@ -25,6 +24,7 @@ impl XmlWriter { } pub fn write_element_start(&mut self, tag: &str) -> Result<()> { + self.set_prefixes.push(BTreeSet::new()); write!(self.inner, "<{}", tag) } @@ -34,7 +34,7 @@ impl XmlWriter { ns: &'static str, ) -> Result<()> { if !self.is_prefix_defined_as(&prefix, ns) { - //self.push_changed_namespace(prefix, ns)?; + self.push_changed_namespace(prefix, ns)?; if let Some(prefix) = prefix { write!(self.inner, r#" xmlns:{}="{}""#, prefix, xml_escape(ns)) } else { @@ -74,12 +74,12 @@ impl XmlWriter { } pub fn write_element_end_close(&mut self, tag: &str) -> Result<()> { - //self.pop_changed_namespaces()?; + self.pop_changed_namespaces()?; write!(self.inner, "", tag) } pub fn write_element_end_empty(&mut self) -> Result<()> { - //self.pop_changed_namespaces()?; + self.pop_changed_namespaces()?; write!(self.inner, "/>") } diff --git a/test-suite/tests/enum.rs b/test-suite/tests/enum.rs index c6fb646..e7f931a 100644 --- a/test-suite/tests/enum.rs +++ b/test-suite/tests/enum.rs @@ -23,8 +23,6 @@ enum AB { #[xml(tag = "b")] B(B), } -// Recursive expansion of XmlRead! macro -// ====================================== #[derive(XmlWrite, XmlRead, PartialEq, Debug)] enum CD { diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index 1957af1..aa9c169 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -3,7 +3,7 @@ use strong_xml::{XmlRead, XmlResult, XmlWrite}; #[test] fn test() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "nested", prefix = "n", ns = "n: http://www.example.com")] + #[xml(tag = "n:nested", ns="n: http://www.example.com")] struct Nested { #[xml(child = "nested")] contents: Vec, @@ -33,22 +33,21 @@ fn test() -> XmlResult<()> { fn test2() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml( - tag = "nested", - prefix = "n", - ns = "n: http://www.example.com", - ns = "n2: http://www.example.com" + tag = "n:nested", + ns="n: http://www.example.com", + ns="n2: http://www.example.com" )] struct Nested { - #[xml(child = "nested", prefix = "n")] + #[xml(child = "n:nested")] nested: Vec, - #[xml(child = "nested2", prefix = "n2")] + #[xml(child = "n2:nested2")] nested2: Nested2, } #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "nested2", prefix = "n2", ns = "n2: http://www.example.com")] + #[xml(tag = "n2:nested2", ns="n2: http://www.example.com")] struct Nested2 { - #[xml(attr = "nest", prefix = "n2")] + #[xml(attr = "n2:nest")] value: String, } @@ -79,28 +78,37 @@ fn test2() -> XmlResult<()> { */ #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "tag", prefix = "a", ns = "a: http://www.example.com")] + #[xml(tag = "a:tag", ns = "a: http://www.example.com")] struct A { #[xml(text)] value: String, } #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "tag", prefix = "b", ns = "b: http://www.example.com/1")] + #[xml(tag = "b:tag", ns = "b: http://www.example.com/1")] struct B { #[xml(text)] value: String, } #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "root", prefix = "a", ns = "a: ns_a", ns = "b: ns_b")] + #[xml(tag = "a:root", ns = "a: ns_a", ns = "b: ns_b")] struct C { - #[xml(child = "tag", prefix = "a")] + #[xml(child = "a:tag")] a: A, - #[xml(child = "tag", prefix = "b")] + #[xml(child = "b:tag")] b: B, } + println!("{}", C { + a: A { + value: "hello".into() + }, + b: B { + value: "world".into() + } + }.to_string()?); + assert_eq!( C { a: A { From a74987ae5839d21e3a906735a59a5025810c1fb1 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Fri, 13 May 2022 17:03:49 -0500 Subject: [PATCH 13/15] Some code clean ups --- strong-xml-derive/src/read/mod.rs | 4 +-- strong-xml-derive/src/types.rs | 2 +- strong-xml-derive/src/write/named.rs | 2 +- strong-xml/src/xml_reader.rs | 2 +- strong-xml/src/xml_writer.rs | 4 +-- test-suite/tests/namespaces.rs | 42 +++++++++++++++++----------- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/strong-xml-derive/src/read/mod.rs b/strong-xml-derive/src/read/mod.rs index 433c8c7..f7911b0 100644 --- a/strong-xml-derive/src/read/mod.rs +++ b/strong-xml-derive/src/read/mod.rs @@ -22,7 +22,7 @@ pub fn impl_read(element: Element) -> TokenStream { tag, name, fields, - namespaces, + namespaces: _, } => named::read(&tag, quote!(#ele_name::#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#ele_name::#name)), }); @@ -49,7 +49,7 @@ pub fn impl_read(element: Element) -> TokenStream { tag, name, fields, - namespaces, + namespaces: _, } => named::read(&tag, quote!(#name), &fields), Fields::Newtype { name, ty, .. } => newtype::read(&ty, quote!(#name)), }, diff --git a/strong-xml-derive/src/types.rs b/strong-xml-derive/src/types.rs index fa8ce62..6ae35cb 100644 --- a/strong-xml-derive/src/types.rs +++ b/strong-xml-derive/src/types.rs @@ -1,6 +1,6 @@ -use std::collections::BTreeMap; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; +use std::collections::BTreeMap; use syn::{Lit::*, Meta::*, *}; use crate::utils::elide_type_lifetimes; diff --git a/strong-xml-derive/src/write/named.rs b/strong-xml-derive/src/write/named.rs index 1d35529..dee21d2 100644 --- a/strong-xml-derive/src/write/named.rs +++ b/strong-xml-derive/src/write/named.rs @@ -2,7 +2,7 @@ use proc_macro2::TokenStream; use quote::quote; use syn::Ident; -use crate::types::{Field, NamespaceDef, NamespaceDefs, QName, Type}; +use crate::types::{Field, NamespaceDefs, QName, Type}; pub fn write( tag: &QName, diff --git a/strong-xml/src/xml_reader.rs b/strong-xml/src/xml_reader.rs index 62d17c4..1bfd9fb 100644 --- a/strong-xml/src/xml_reader.rs +++ b/strong-xml/src/xml_reader.rs @@ -158,7 +158,7 @@ impl<'a> XmlReader<'a> { span, }) if end_tag.is_some() => { let end_tag = end_tag.unwrap(); - let tag = &span[2..span.len() - 1];// remove `` + let tag = &span[2..span.len() - 1]; // remove `` if tag == end_tag { self.next(); return Ok(None); diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index 33a3768..ccfc14a 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -143,8 +143,8 @@ impl XmlWriter { ) -> Result<()> { use std::io::{Error, ErrorKind}; - let set_prefixes = if let Some(blah) = self.set_prefixes.last_mut() { - blah + let set_prefixes = if let Some(prefixes) = self.set_prefixes.last_mut() { + prefixes } else { return Err(Error::new( ErrorKind::Other, diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index aa9c169..7224f35 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -3,7 +3,7 @@ use strong_xml::{XmlRead, XmlResult, XmlWrite}; #[test] fn test() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "n:nested", ns="n: http://www.example.com")] + #[xml(tag = "n:nested", ns = "n: http://www.example.com")] struct Nested { #[xml(child = "nested")] contents: Vec, @@ -34,8 +34,8 @@ fn test2() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml( tag = "n:nested", - ns="n: http://www.example.com", - ns="n2: http://www.example.com" + ns = "n: http://www.example.com", + ns = "n2: http://www.example.com" )] struct Nested { #[xml(child = "n:nested")] @@ -45,7 +45,7 @@ fn test2() -> XmlResult<()> { } #[derive(XmlWrite, XmlRead, PartialEq, Debug)] - #[xml(tag = "n2:nested2", ns="n2: http://www.example.com")] + #[xml(tag = "n2:nested2", ns = "n2: http://www.example.com")] struct Nested2 { #[xml(attr = "n2:nest")] value: String, @@ -67,15 +67,17 @@ fn test2() -> XmlResult<()> { r#""# ); - /* assert_eq!( (Nested { nested: vec![], - nested2: Nested2 { value: "hello world".into() } + nested2: Nested2 { + value: "hello world".into() + } }), - Nested::from_str(r#""#)? + Nested::from_str( + r#""# + )? ); - */ #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml(tag = "a:tag", ns = "a: http://www.example.com")] @@ -100,15 +102,23 @@ fn test2() -> XmlResult<()> { b: B, } - println!("{}", C { - a: A { - value: "hello".into() - }, - b: B { - value: "world".into() + assert_eq!( + C { + a: A { + value: "hello".into() + }, + b: B { + value: "world".into() + } } - }.to_string()?); + .to_string()?, + r#"helloworld"# + ); + /* + TODO: namespaces are not enforced when readeing. + Should we have options that the user can set? + */ assert_eq!( C { a: A { @@ -119,7 +129,7 @@ fn test2() -> XmlResult<()> { } }, C::from_str( - r#"helloworld"# + r#"helloworld"# )? ); From 2a0cfee95af21c12b64a106c4c154e670cb60718 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Mon, 16 May 2022 14:20:11 -0500 Subject: [PATCH 14/15] Added some comments --- strong-xml/src/xml_writer.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/strong-xml/src/xml_writer.rs b/strong-xml/src/xml_writer.rs index ccfc14a..01b65ad 100644 --- a/strong-xml/src/xml_writer.rs +++ b/strong-xml/src/xml_writer.rs @@ -24,6 +24,7 @@ impl XmlWriter { } pub fn write_element_start(&mut self, tag: &str) -> Result<()> { + // Add new level to store set prefixes for this scope self.set_prefixes.push(BTreeSet::new()); write!(self.inner, "<{}", tag) } @@ -34,6 +35,7 @@ impl XmlWriter { ns: &'static str, ) -> Result<()> { if !self.is_prefix_defined_as(&prefix, ns) { + // let writer know that there has been a prefix that has been re/defined. self.push_changed_namespace(prefix, ns)?; if let Some(prefix) = prefix { write!(self.inner, r#" xmlns:{}="{}""#, prefix, xml_escape(ns)) @@ -74,11 +76,13 @@ impl XmlWriter { } pub fn write_element_end_close(&mut self, tag: &str) -> Result<()> { + // Restore the previous namespace declarations self.pop_changed_namespaces()?; write!(self.inner, "", tag) } pub fn write_element_end_empty(&mut self) -> Result<()> { + // Restore the previous namespace declarations self.pop_changed_namespaces()?; write!(self.inner, "/>") } @@ -105,6 +109,9 @@ impl XmlWriter { fn pop_changed_namespaces(&mut self) -> Result<()> { use std::io::{Error, ErrorKind}; + + // Go and get the current set_prefixes for this scope + // and pop of the last namespace for each prefix if let Some(set_prefixes) = self.set_prefixes.pop() { set_prefixes .iter() @@ -143,6 +150,7 @@ impl XmlWriter { ) -> Result<()> { use std::io::{Error, ErrorKind}; + // Get the current prefix scope off of the stack let set_prefixes = if let Some(prefixes) = self.set_prefixes.last_mut() { prefixes } else { @@ -152,6 +160,8 @@ impl XmlWriter { )); }; + // Push prefix onto stack for namespace + // or create new stack with namespace as only element if let Some(v) = self.used_namespaces.get_mut(&prefix) { v.push(namespace); } else { From c713e80612fcad8f6b942880c27528cdd01da042 Mon Sep 17 00:00:00 2001 From: TheSchemm Date: Mon, 16 May 2022 14:20:22 -0500 Subject: [PATCH 15/15] Test fixes --- test-suite/tests/namespaces.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-suite/tests/namespaces.rs b/test-suite/tests/namespaces.rs index 7224f35..1002bf6 100644 --- a/test-suite/tests/namespaces.rs +++ b/test-suite/tests/namespaces.rs @@ -1,11 +1,11 @@ use strong_xml::{XmlRead, XmlResult, XmlWrite}; #[test] -fn test() -> XmlResult<()> { +fn test_nested_namespaces() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml(tag = "n:nested", ns = "n: http://www.example.com")] struct Nested { - #[xml(child = "nested")] + #[xml(child = "n:nested")] contents: Vec, } @@ -30,7 +30,7 @@ fn test() -> XmlResult<()> { } #[test] -fn test2() -> XmlResult<()> { +fn test_namespace_clashes() -> XmlResult<()> { #[derive(XmlWrite, XmlRead, PartialEq, Debug)] #[xml( tag = "n:nested",