Use Id type to make namespaces explicit part of node identity
This commit is contained in:
parent
f42f9fd811
commit
921e094925
|
@ -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)?);
|
||||
},
|
||||
));
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue