Use Id type to make namespaces explicit part of node identity
This commit is contained in:
parent
c465bbcc98
commit
34ab6121ff
|
@ -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)?);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
|
@ -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,6 +54,23 @@ impl<'xml> Deserializer<'xml> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Check if defined and gotten namespaces equals for each field
|
// Check if defined and gotten namespaces equals for each field
|
||||||
pub fn compare_namespace(
|
pub fn compare_namespace(
|
||||||
&self,
|
&self,
|
||||||
|
@ -72,8 +89,27 @@ impl<'xml> Deserializer<'xml> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +240,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 +332,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 +379,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 +410,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 },
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue