Use Id type to make namespaces explicit part of node identity

This commit is contained in:
Dirkjan Ochtman 2022-09-04 23:02:36 +02:00
parent f42f9fd811
commit 921e094925
5 changed files with 130 additions and 63 deletions

View File

@ -156,10 +156,10 @@ impl Deserializer {
use ::instant_xml::de::Node; use ::instant_xml::de::Node;
#declare_values #declare_values
while let Some(( key, _ )) = deserializer.peek_next_attribute() { while let Some(attr) = deserializer.peek_next_attribute()? {
let attr = { let attr = {
#attributes_consts #attributes_consts
match *key { match attr.id {
#attributes_names #attributes_names
_ => __Attributes::__Ignore _ => __Attributes::__Ignore
} }
@ -167,16 +167,17 @@ impl Deserializer {
match attr { match attr {
#attr_type_match #attr_type_match
__Attributes::__Ignore => todo!(), __Attributes::__Ignore => {}
} }
} }
while let Some(node) = deserializer.peek_next_tag()? { while let Some(node) = deserializer.peek_next_tag()? {
match node { match node {
Node::Open { ns, name } => { Node::Open { ns, name } => {
let id = ::instant_xml::Id { ns, name };
let element = { let element = {
#elements_consts #elements_consts
match name { match id {
#elements_names #elements_names
_ => __Elements::__Ignore _ => __Elements::__Ignore
} }
@ -184,7 +185,9 @@ impl Deserializer {
match element { match element {
#elem_type_match #elem_type_match
__Elements::__Ignore => panic!("No such element"), __Elements::__Ignore => {
deserializer.ignore(id)?;
}
} }
} }
Node::Close { name } => { Node::Close { name } => {
@ -216,7 +219,10 @@ impl Deserializer {
)); ));
out.extend(quote!( out.extend(quote!(
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element(#name); const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element(::instant_xml::Id {
ns: #default_namespace,
name: #name,
});
)); ));
out = quote!( out = quote!(
@ -247,8 +253,24 @@ impl Deserializer {
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site()); let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
tokens.enum_.extend(quote!(#enum_name,)); tokens.enum_.extend(quote!(#enum_name,));
let default_ns = match &field_meta.ns.default {
Namespace::Default => &container_meta.ns.default,
_ => &field_meta.ns.default,
};
let ns = match default_ns {
Namespace::Default => "",
Namespace::Prefix(prefix) => match container_meta.ns.prefixes.get(prefix) {
Some(ns) => ns,
None => panic!("undefined prefix {prefix} in xml attribute"),
},
Namespace::Literal(ns) => ns,
};
tokens.consts.extend(quote!( tokens.consts.extend(quote!(
const #const_field_var_str: &str = <#no_lifetime_type>::KIND.name(#field_var_str); const #const_field_var_str: ::instant_xml::Id<'static> = <#no_lifetime_type>::KIND.name(
::instant_xml::Id { ns: #ns, name: #field_var_str }
);
)); ));
if !field_meta.attribute { if !field_meta.attribute {
@ -265,20 +287,6 @@ impl Deserializer {
let mut #enum_name: Option<#no_lifetime_type> = None; let mut #enum_name: Option<#no_lifetime_type> = None;
)); ));
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 { if !field_meta.attribute {
tokens.match_.extend(quote!( tokens.match_.extend(quote!(
__Elements::#enum_name => { __Elements::#enum_name => {
@ -286,10 +294,6 @@ impl Deserializer {
panic!("duplicated value"); panic!("duplicated value");
} }
if Some(ns) != #new_default_ns {
return Err(Error::WrongNamespace);
}
#enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?); #enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?);
}, },
)); ));

View File

@ -1,16 +1,16 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::Peekable; use std::iter::Peekable;
use super::Error; use super::{Error, Id};
use xmlparser::{ElementEnd, Token, Tokenizer}; use xmlparser::{ElementEnd, Token, Tokenizer};
pub struct Deserializer<'xml> { pub struct Deserializer<'xml> {
parser: Peekable<XmlParser<'xml>>, parser: Peekable<XmlParser<'xml>>,
def_namespaces: HashMap<&'xml str, &'xml str>, def_namespaces: HashMap<&'xml str, &'xml str>,
parser_namespaces: HashMap<&'xml str, &'xml str>, pub parser_namespaces: HashMap<&'xml str, &'xml str>,
def_default_namespace: &'xml str, def_default_namespace: &'xml str,
parser_default_namespace: &'xml str, parser_default_namespace: &'xml str,
tag_attributes: Vec<(&'xml str, &'xml str)>, tag_attributes: Vec<Attribute<'xml>>,
next_type: EntityType, next_type: EntityType,
} }
@ -54,26 +54,44 @@ impl<'xml> Deserializer<'xml> {
})) }))
} }
// Check if defined and gotten namespaces equals for each field pub fn id(&self, item: &TagData<'xml>) -> Result<Id<'xml>, Error> {
pub fn compare_namespace( let ns = match (item.ns, item.prefix) {
&self, (Some(_), Some(_)) => return Err(Error::WrongNamespace),
expected: &Option<&str>, (Some(ns), None) => ns,
actual: Option<&str>, (None, Some(prefix)) => match self.parser_namespaces.get(prefix) {
) -> Result<(), Error> { Some(ns) => ns,
match (expected, actual) { None => return Err(Error::WrongNamespace),
(Some(expected), Some(actual)) => { },
match self.parser_namespaces.get(expected) == self.def_namespaces.get(actual) { (None, None) => "",
true => Ok(()), };
false => Err(Error::WrongNamespace),
} Ok(Id {
} ns,
(Some(_), None) | (None, Some(_)) => Err(Error::WrongNamespace), name: &item.key,
(None, None) => Ok(()), })
}
} }
pub fn peek_next_attribute(&self) -> Option<&(&'xml str, &'xml str)> { pub fn peek_next_attribute(&self) -> Result<Option<AttributeNode<'xml>>, Error> {
self.tag_attributes.last() let attr = match self.tag_attributes.last() {
Some(attr) => attr,
None => return Ok(None),
};
let ns = match attr.prefix {
Some(key) => match self.parser_namespaces.get(key) {
Some(ns) => ns,
None => return Err(Error::WrongNamespace),
},
None => self.parser_default_namespace,
};
Ok(Some(AttributeNode {
id: Id {
ns,
name: attr.local,
},
value: attr.value,
}))
} }
pub fn deserialize_struct<V>( pub fn deserialize_struct<V>(
@ -204,10 +222,34 @@ impl<'xml> Deserializer<'xml> {
V: Visitor<'xml>, V: Visitor<'xml>,
{ {
match self.tag_attributes.pop() { match self.tag_attributes.pop() {
Some((_, value)) => visitor.visit_str(value), Some(attr) => visitor.visit_str(attr.value),
None => Err(Error::UnexpectedEndOfStream), None => Err(Error::UnexpectedEndOfStream),
} }
} }
pub fn ignore(&mut self, id: Id<'xml>) -> Result<(), Error> {
let mut levels = 0;
while let Some(result) = self.parser.next() {
match result? {
XmlRecord::Open(item) => {
if self.id(&item)? == id {
levels += 1;
}
}
XmlRecord::Close(item) => {
if item == id.name {
levels -= 1;
if levels == 0 {
return Ok(());
}
}
}
_ => {}
}
}
Ok(())
}
} }
pub struct XmlParser<'xml> { pub struct XmlParser<'xml> {
@ -272,12 +314,12 @@ impl<'xml> Iterator for XmlParser<'xml> {
let mut attributes = Vec::new(); let mut attributes = Vec::new();
loop { loop {
let item = match self.iter.next() { let token = match self.iter.next() {
Some(v) => v, Some(v) => v,
None => return None, None => return None,
}; };
match item { match token {
Ok(Token::ElementStart { prefix, local, .. }) => { Ok(Token::ElementStart { prefix, local, .. }) => {
key = Some(local.as_str()); key = Some(local.as_str());
prefix_ret = match prefix.is_empty() { prefix_ret = match prefix.is_empty() {
@ -319,12 +361,13 @@ impl<'xml> Iterator for XmlParser<'xml> {
} else if prefix.as_str() == "xmlns" { } else if prefix.as_str() == "xmlns" {
// Namespaces // Namespaces
namespaces.insert(local.as_str(), value.as_str()); namespaces.insert(local.as_str(), value.as_str());
} else if prefix.is_empty() {
// Other attributes
attributes.push((local.as_str(), value.as_str()));
} else { } else {
// TODO: Can the attributes have the prefix? let prefix = (!prefix.is_empty()).then_some(prefix.as_str());
todo!(); attributes.push(Attribute {
prefix,
local: local.as_str(),
value: value.as_str(),
});
} }
} }
Ok(Token::Text { text }) => { Ok(Token::Text { text }) => {
@ -349,20 +392,34 @@ pub trait Visitor<'xml>: Sized {
} }
} }
#[derive(Debug)]
pub enum XmlRecord<'xml> { pub enum XmlRecord<'xml> {
Open(TagData<'xml>), Open(TagData<'xml>),
Element(&'xml str), Element(&'xml str),
Close(&'xml str), Close(&'xml str),
} }
#[derive(Debug)]
pub struct TagData<'xml> { pub struct TagData<'xml> {
pub key: &'xml str, pub key: &'xml str,
pub attributes: Vec<(&'xml str, &'xml str)>, pub attributes: Vec<Attribute<'xml>>,
pub ns: Option<&'xml str>, pub ns: Option<&'xml str>,
pub prefixes: HashMap<&'xml str, &'xml str>, pub prefixes: HashMap<&'xml str, &'xml str>,
pub prefix: Option<&'xml str>, pub prefix: Option<&'xml str>,
} }
pub struct AttributeNode<'xml> {
pub id: Id<'xml>,
pub value: &'xml str,
}
#[derive(Debug)]
pub struct Attribute<'xml> {
pub prefix: Option<&'xml str>,
pub local: &'xml str,
pub value: &'xml str,
}
pub enum Node<'xml> { pub enum Node<'xml> {
Open { ns: &'xml str, name: &'xml str }, Open { ns: &'xml str, name: &'xml str },
Close { name: &'xml str }, Close { name: &'xml str },

View File

@ -48,18 +48,24 @@ pub trait FromXml<'xml>: Sized {
pub enum Kind { pub enum Kind {
Scalar, Scalar,
Element(&'static str), Element(Id<'static>),
} }
impl Kind { impl Kind {
pub const fn name(&self, field: &'static str) -> &'static str { pub const fn name<'a>(&self, field: Id<'static>) -> Id<'static> {
match self { match self {
Kind::Scalar => field, Kind::Scalar => field,
Kind::Element(name) => name, Kind::Element(name) => *name,
} }
} }
} }
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Id<'a> {
pub ns: &'a str,
pub name: &'a str,
}
pub trait FromXmlOwned: for<'xml> FromXml<'xml> {} pub trait FromXmlOwned: for<'xml> FromXml<'xml> {}
#[derive(Clone, Debug, Eq, Error, PartialEq)] #[derive(Clone, Debug, Eq, Error, PartialEq)]

View File

@ -26,7 +26,7 @@ fn direct_namespaces() {
"<StructDirectNamespace xmlns=\"URI\"><flag xmlns=\"WRONG\">true</flag></StructDirectNamespace>" "<StructDirectNamespace xmlns=\"URI\"><flag xmlns=\"WRONG\">true</flag></StructDirectNamespace>"
) )
.unwrap_err(), .unwrap_err(),
Error::WrongNamespace Error::MissingValue
); );
// Wrong direct namespace - missing namespace // Wrong direct namespace - missing namespace
@ -35,6 +35,6 @@ fn direct_namespaces() {
"<StructDirectNamespace xmlns=\"URI\"><flag>true</flag></StructDirectNamespace>" "<StructDirectNamespace xmlns=\"URI\"><flag>true</flag></StructDirectNamespace>"
) )
.unwrap_err(), .unwrap_err(),
Error::WrongNamespace Error::MissingValue
); );
} }

View File

@ -75,7 +75,7 @@ fn default_namespaces() {
// Wrong child namespace // Wrong child namespace
assert_eq!( assert_eq!(
StructWithWrongNestedNamespace::from_xml("<StructWithWrongNestedNamespace xmlns=\"URI\" xmlns:dar=\"BAZ\"><NestedWrongNamespace><flag>true</flag></NestedWrongNamespace></StructWithWrongNestedNamespace>").unwrap_err(), StructWithWrongNestedNamespace::from_xml("<StructWithWrongNestedNamespace xmlns=\"URI\" xmlns:dar=\"BAZ\"><NestedWrongNamespace><flag>true</flag></NestedWrongNamespace></StructWithWrongNestedNamespace>").unwrap_err(),
Error::WrongNamespace Error::MissingValue
); );
} }
@ -118,7 +118,7 @@ fn other_namespaces() {
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"WRONG\"><bar:flag>true</bar:flag></NestedOtherNamespace>" "<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"WRONG\"><bar:flag>true</bar:flag></NestedOtherNamespace>"
) )
.unwrap_err(), .unwrap_err(),
Error::WrongNamespace Error::MissingValue
); );
// Other namespace not-nested - missing parser prefix // Other namespace not-nested - missing parser prefix
@ -127,7 +127,7 @@ fn other_namespaces() {
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAR\"><flag>true</flag></NestedOtherNamespace>" "<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAR\"><flag>true</flag></NestedOtherNamespace>"
) )
.unwrap_err(), .unwrap_err(),
Error::WrongNamespace Error::MissingValue
); );
// Correct child other namespace // Correct child other namespace