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;
#declare_values
while let Some(( key, _ )) = deserializer.peek_next_attribute() {
while let Some(attr) = deserializer.peek_next_attribute()? {
let attr = {
#attributes_consts
match *key {
match attr.id {
#attributes_names
_ => __Attributes::__Ignore
}
@ -167,16 +167,17 @@ impl Deserializer {
match attr {
#attr_type_match
__Attributes::__Ignore => todo!(),
__Attributes::__Ignore => {}
}
}
while let Some(node) = deserializer.peek_next_tag()? {
match node {
Node::Open { ns, name } => {
let id = ::instant_xml::Id { ns, name };
let element = {
#elements_consts
match name {
match id {
#elements_names
_ => __Elements::__Ignore
}
@ -184,7 +185,9 @@ impl Deserializer {
match element {
#elem_type_match
__Elements::__Ignore => panic!("No such element"),
__Elements::__Ignore => {
deserializer.ignore(id)?;
}
}
}
Node::Close { name } => {
@ -216,7 +219,10 @@ impl Deserializer {
));
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!(
@ -247,8 +253,24 @@ impl Deserializer {
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
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!(
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 {
@ -265,20 +287,6 @@ impl Deserializer {
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 {
tokens.match_.extend(quote!(
__Elements::#enum_name => {
@ -286,10 +294,6 @@ impl Deserializer {
panic!("duplicated value");
}
if Some(ns) != #new_default_ns {
return Err(Error::WrongNamespace);
}
#enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?);
},
));

View File

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

View File

@ -48,18 +48,24 @@ pub trait FromXml<'xml>: Sized {
pub enum Kind {
Scalar,
Element(&'static str),
Element(Id<'static>),
}
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 {
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> {}
#[derive(Clone, Debug, Eq, Error, PartialEq)]

View File

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

View File

@ -75,7 +75,7 @@ fn default_namespaces() {
// Wrong child namespace
assert_eq!(
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>"
)
.unwrap_err(),
Error::WrongNamespace
Error::MissingValue
);
// Other namespace not-nested - missing parser prefix
@ -127,7 +127,7 @@ fn other_namespaces() {
"<NestedOtherNamespace xmlns=\"URI\" xmlns:bar=\"BAR\"><flag>true</flag></NestedOtherNamespace>"
)
.unwrap_err(),
Error::WrongNamespace
Error::MissingValue
);
// Correct child other namespace