From f42f9fd811e90cb8ba372889a4e8bdb77eca54ea Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sun, 4 Sep 2022 22:19:59 +0200 Subject: [PATCH] Introduce Node layer to gain full access to parsed element --- instant-xml-macros/src/de.rs | 47 +++++++++++++++++++---------- instant-xml/src/de.rs | 57 ++++++++++++++++++++++-------------- instant-xml/src/lib.rs | 2 +- 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/instant-xml-macros/src/de.rs b/instant-xml-macros/src/de.rs index 75dfda3..158f10a 100644 --- a/instant-xml-macros/src/de.rs +++ b/instant-xml-macros/src/de.rs @@ -35,8 +35,8 @@ impl Deserializer { pub fn new(input: &syn::DeriveInput) -> Deserializer { let ident = &input.ident; let container_meta = ContainerMeta::from_derive(input); - let default_namespace = match container_meta.ns.default { - Namespace::Default => String::new(), + let default_namespace = match &container_meta.ns.default { + Namespace::Default => "", Namespace::Prefix(_) => panic!("container namespace cannot be prefix"), Namespace::Literal(ns) => ns, }; @@ -103,6 +103,7 @@ impl Deserializer { &mut return_val, tokens, field_meta, + &container_meta, ); }); } @@ -152,6 +153,8 @@ impl Deserializer { &self, deserializer: &mut ::instant_xml::Deserializer<'xml> ) -> Result { + use ::instant_xml::de::Node; + #declare_values while let Some(( key, _ )) = deserializer.peek_next_attribute() { let attr = { @@ -167,12 +170,13 @@ impl Deserializer { __Attributes::__Ignore => todo!(), } } - while let Some(item) = &deserializer.peek_next_tag()? { - match item { - XmlRecord::Open(item) => { + + while let Some(node) = deserializer.peek_next_tag()? { + match node { + Node::Open { ns, name } => { let element = { #elements_consts - match item.key.as_ref() { + match name { #elements_names _ => __Elements::__Ignore } @@ -182,13 +186,13 @@ impl Deserializer { #elem_type_match __Elements::__Ignore => panic!("No such element"), } - } - XmlRecord::Close(tag) => { - if tag == &#name { + } + Node::Close { name } => { + if name == #name { break; } }, - XmlRecord::Element(_) => panic!("Unexpected element"), + Node::Text { text } => panic!("Unexpected element"), } } @@ -232,6 +236,7 @@ impl Deserializer { return_val: &mut TokenStream, tokens: &mut Tokens, field_meta: FieldMeta, + container_meta: &ContainerMeta, ) { let field_var = field.ident.as_ref().unwrap(); let field_var_str = field_var.to_string(); @@ -260,10 +265,18 @@ impl Deserializer { let mut #enum_name: Option<#no_lifetime_type> = None; )); - let (field_prefix, new_default_ns) = match field_meta.ns.default { - Namespace::Default => (quote!(None::<&str>), quote!(None::<&str>)), - Namespace::Prefix(prefix) => (quote!(Some(#prefix)), quote!(None)), - Namespace::Literal(ns) => (quote!(None::<&str>), quote!(Some(#ns))), + let default_ns = match field_meta.ns.default { + Namespace::Default => &container_meta.ns.default, + _ => &field_meta.ns.default, + }; + + let new_default_ns = match default_ns { + Namespace::Default => quote!(None), + Namespace::Prefix(prefix) => match container_meta.ns.prefixes.get(prefix) { + Some(ns) => quote!(Some(#ns)), + None => panic!("invalid prefix for xml attribute"), + }, + Namespace::Literal(ns) => quote!(Some(#ns)), }; if !field_meta.attribute { @@ -273,8 +286,10 @@ impl Deserializer { panic!("duplicated value"); } - deserializer.compare_namespace(&item.prefix, #field_prefix)?; - deserializer.set_next_def_namespace(#new_default_ns)?; + if Some(ns) != #new_default_ns { + return Err(Error::WrongNamespace); + } + #enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?); }, )); diff --git a/instant-xml/src/de.rs b/instant-xml/src/de.rs index bd5b165..81b084e 100644 --- a/instant-xml/src/de.rs +++ b/instant-xml/src/de.rs @@ -5,32 +5,53 @@ use super::Error; use xmlparser::{ElementEnd, Token, Tokenizer}; pub struct Deserializer<'xml> { - parser: XmlParser<'xml>, + parser: Peekable>, def_namespaces: HashMap<&'xml str, &'xml str>, parser_namespaces: HashMap<&'xml str, &'xml str>, def_default_namespace: &'xml str, parser_default_namespace: &'xml str, tag_attributes: Vec<(&'xml str, &'xml str)>, next_type: EntityType, - next_def_namespace: Option<&'xml str>, } impl<'xml> Deserializer<'xml> { pub fn new(input: &'xml str) -> Self { Self { - parser: XmlParser::new(input), + parser: XmlParser::new(input).peekable(), def_namespaces: std::collections::HashMap::new(), parser_namespaces: std::collections::HashMap::new(), def_default_namespace: "", parser_default_namespace: "", tag_attributes: Vec::new(), next_type: EntityType::Element, - next_def_namespace: None, } } - pub fn peek_next_tag(&mut self) -> Result>, Error> { - self.parser.peek_next_tag() + pub fn peek_next_tag(&mut self) -> Result>, Error> { + let record = match self.parser.peek() { + Some(Ok(record)) => record, + Some(Err(err)) => return Err(err.clone()), + None => return Ok(None), + }; + + Ok(Some(match record { + XmlRecord::Open(TagData { + key, ns, prefix, .. + }) => { + let ns = match (ns, prefix) { + (_, Some(prefix)) => match self.parser_namespaces.get(prefix) { + Some(ns) => ns, + None => return Err(Error::WrongNamespace), + }, + (Some(ns), None) => ns, + (None, None) => self.parser_default_namespace, + }; + + Node::Open { ns, name: key } + } + XmlRecord::Element(text) => Node::Text { text }, + XmlRecord::Close(name) => Node::Close { name }, + })) } // Check if defined and gotten namespaces equals for each field @@ -158,30 +179,16 @@ impl<'xml> Deserializer<'xml> { ret } - pub fn set_next_def_namespace(&mut self, namespace: Option<&'xml str>) -> Result<(), Error> { - if self.next_def_namespace.is_some() { - return Err(Error::UnexpectedState); - } - - self.next_def_namespace = namespace; - Ok(()) - } - pub(crate) fn deserialize_element(&mut self, visitor: V) -> Result where V: Visitor<'xml>, { // Process open tag - let tag_data = match self.parser.next() { - Some(Ok(XmlRecord::Open(item))) => item, + match self.parser.next() { + Some(Ok(XmlRecord::Open(_))) => {} _ => return Err(Error::UnexpectedValue), }; - if tag_data.ns != self.next_def_namespace { - return Err(Error::WrongNamespace); - } - - self.next_def_namespace = None; match self.parser.next() { Some(Ok(XmlRecord::Element(v))) => { let ret = visitor.visit_str(v); @@ -356,6 +363,12 @@ pub struct TagData<'xml> { pub prefix: Option<&'xml str>, } +pub enum Node<'xml> { + Open { ns: &'xml str, name: &'xml str }, + Close { name: &'xml str }, + Text { text: &'xml str }, +} + #[derive(Clone, PartialEq, Eq)] pub enum EntityType { Element, diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index c3ad042..fd79cdf 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -62,7 +62,7 @@ impl Kind { pub trait FromXmlOwned: for<'xml> FromXml<'xml> {} -#[derive(Debug, Error, PartialEq, Eq)] +#[derive(Clone, Debug, Eq, Error, PartialEq)] pub enum Error { #[error("format: {0}")] Format(#[from] fmt::Error),