Simple deserializer (#7)
This commit is contained in:
parent
b0e09962bd
commit
ba40445c5e
|
@ -0,0 +1,254 @@
|
||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use crate::{get_namespaces, retrieve_attr};
|
||||||
|
|
||||||
|
struct Tokens {
|
||||||
|
enum_: TokenStream,
|
||||||
|
consts: TokenStream,
|
||||||
|
names: TokenStream,
|
||||||
|
match_: TokenStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Tokens {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enum_: TokenStream::new(),
|
||||||
|
consts: TokenStream::new(),
|
||||||
|
names: TokenStream::new(),
|
||||||
|
match_: TokenStream::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Deserializer {
|
||||||
|
out: TokenStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl quote::ToTokens for Deserializer {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
tokens.extend(self.out.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserializer {
|
||||||
|
pub fn new(input: &syn::DeriveInput) -> Deserializer {
|
||||||
|
let ident = &input.ident;
|
||||||
|
let name = ident.to_string();
|
||||||
|
let mut out = TokenStream::new();
|
||||||
|
|
||||||
|
let (_, other_namespaces) = get_namespaces(&input.attrs);
|
||||||
|
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
|
||||||
|
for (k, v) in other_namespaces.iter() {
|
||||||
|
namespaces_map.extend(quote!(
|
||||||
|
namespaces_map.insert(#k, #v);
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Varying values
|
||||||
|
let mut elements_tokens = Tokens::default();
|
||||||
|
let mut attributes_tokens = Tokens::default();
|
||||||
|
|
||||||
|
// Common values
|
||||||
|
let mut declare_values = TokenStream::new();
|
||||||
|
let mut return_val = TokenStream::new();
|
||||||
|
|
||||||
|
match &input.data {
|
||||||
|
syn::Data::Struct(ref data) => {
|
||||||
|
match data.fields {
|
||||||
|
syn::Fields::Named(ref fields) => {
|
||||||
|
fields.named.iter().enumerate().for_each(|(index, field)| {
|
||||||
|
let is_element;
|
||||||
|
let tokens = match retrieve_attr("attribute", &field.attrs) {
|
||||||
|
Some(true) => {
|
||||||
|
is_element = false;
|
||||||
|
&mut attributes_tokens
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
is_element = true;
|
||||||
|
&mut elements_tokens
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::process_field(
|
||||||
|
field,
|
||||||
|
index,
|
||||||
|
&mut declare_values,
|
||||||
|
&mut return_val,
|
||||||
|
tokens,
|
||||||
|
is_element,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
syn::Fields::Unnamed(_) => todo!(),
|
||||||
|
syn::Fields::Unit => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Elements
|
||||||
|
let elements_enum = elements_tokens.enum_;
|
||||||
|
let elements_consts = elements_tokens.consts;
|
||||||
|
let elements_names = elements_tokens.names;
|
||||||
|
let elem_type_match = elements_tokens.match_;
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
let attributes_enum = attributes_tokens.enum_;
|
||||||
|
let attributes_consts = attributes_tokens.consts;
|
||||||
|
let attributes_names = attributes_tokens.names;
|
||||||
|
let attr_type_match = attributes_tokens.match_;
|
||||||
|
|
||||||
|
out.extend(quote!(
|
||||||
|
fn deserialize(deserializer: &mut ::instant_xml::Deserializer) -> Result<Self, ::instant_xml::Error> {
|
||||||
|
use ::instant_xml::parse::XmlRecord;
|
||||||
|
use ::instant_xml::{Error, Deserializer, Visitor} ;
|
||||||
|
|
||||||
|
enum __Elements {
|
||||||
|
#elements_enum
|
||||||
|
__Ignore,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_element(value: &str) -> __Elements {
|
||||||
|
#elements_consts
|
||||||
|
match value {
|
||||||
|
#elements_names
|
||||||
|
_ => __Elements::__Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum __Attributes {
|
||||||
|
#attributes_enum
|
||||||
|
__Ignore,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_attribute(value: &str) -> __Attributes {
|
||||||
|
#attributes_consts
|
||||||
|
match value {
|
||||||
|
#attributes_names
|
||||||
|
_ => __Attributes::__Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StructVisitor;
|
||||||
|
impl<'xml> Visitor<'xml> for StructVisitor {
|
||||||
|
type Value = #ident;
|
||||||
|
|
||||||
|
fn visit_struct<'a>(&self, deserializer: &mut ::instant_xml::Deserializer) -> Result<Self::Value, ::instant_xml::Error>
|
||||||
|
{
|
||||||
|
#declare_values
|
||||||
|
|
||||||
|
while let Some(( key, _ )) = deserializer.peek_next_attribute() {
|
||||||
|
match get_attribute(&key) {
|
||||||
|
#attr_type_match
|
||||||
|
__Attributes::__Ignore => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while let Some(item) = &deserializer.peek_next_tag()? {
|
||||||
|
match item {
|
||||||
|
XmlRecord::Open(item) => {
|
||||||
|
match get_element(&item.key.as_ref()) {
|
||||||
|
#elem_type_match
|
||||||
|
__Elements::__Ignore => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XmlRecord::Close(tag) => {
|
||||||
|
if tag == &#name {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
XmlRecord::Element(_) => panic!("Unexpected element"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::Value {
|
||||||
|
#return_val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#namespaces_map;
|
||||||
|
deserializer.deserialize_struct(StructVisitor{}, #name, &namespaces_map)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
out.extend(quote!(
|
||||||
|
const TAG_NAME: ::instant_xml::TagName<'xml> = ::instant_xml::TagName::Custom(#name);
|
||||||
|
));
|
||||||
|
|
||||||
|
Deserializer { out }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_field(
|
||||||
|
field: &syn::Field,
|
||||||
|
index: usize,
|
||||||
|
declare_values: &mut TokenStream,
|
||||||
|
return_val: &mut TokenStream,
|
||||||
|
tokens: &mut Tokens,
|
||||||
|
is_element: bool,
|
||||||
|
) {
|
||||||
|
let field_var = field.ident.as_ref().unwrap();
|
||||||
|
let field_var_str = field_var.to_string();
|
||||||
|
let const_field_var_str = Ident::new(&field_var_str.to_uppercase(), Span::call_site());
|
||||||
|
let field_type = match &field.ty {
|
||||||
|
syn::Type::Path(v) => v.path.get_ident(),
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
|
||||||
|
tokens.enum_.extend(quote!(#enum_name,));
|
||||||
|
|
||||||
|
tokens.consts.extend(quote!(
|
||||||
|
const #const_field_var_str: &str = match #field_type::TAG_NAME {
|
||||||
|
::instant_xml::TagName::FieldName => #field_var_str,
|
||||||
|
::instant_xml::TagName::Custom(v) => v,
|
||||||
|
};
|
||||||
|
));
|
||||||
|
|
||||||
|
if is_element {
|
||||||
|
tokens.names.extend(quote!(
|
||||||
|
#const_field_var_str => __Elements::#enum_name,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
tokens.names.extend(quote!(
|
||||||
|
#const_field_var_str => __Attributes::#enum_name,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_values.extend(quote!(
|
||||||
|
let mut #enum_name: Option<#field_type> = None;
|
||||||
|
));
|
||||||
|
|
||||||
|
if is_element {
|
||||||
|
tokens.match_.extend(quote!(
|
||||||
|
__Elements::#enum_name => {
|
||||||
|
if #enum_name.is_some() {
|
||||||
|
panic!("duplicated value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item) = item.prefix {
|
||||||
|
let prefix = item.to_owned();
|
||||||
|
deserializer.verify_namespace(&prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
#enum_name = Some(#field_type::deserialize(deserializer)?);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
tokens.match_.extend(quote!(
|
||||||
|
__Attributes::#enum_name => {
|
||||||
|
if #enum_name.is_some() {
|
||||||
|
panic!("duplicated value");
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.set_next_type_as_attribute()?;
|
||||||
|
#enum_name = Some(#field_type::deserialize(deserializer)?);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return_val.extend(quote!(
|
||||||
|
#field_var: #enum_name.expect("Expected some value"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
mod de;
|
||||||
mod se;
|
mod se;
|
||||||
|
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::collections::{BTreeSet, HashMap};
|
||||||
|
@ -12,7 +13,7 @@ use crate::se::Serializer;
|
||||||
|
|
||||||
const XML: &str = "xml";
|
const XML: &str = "xml";
|
||||||
|
|
||||||
enum FieldAttribute {
|
pub(crate) enum FieldAttribute {
|
||||||
Namespace(String),
|
Namespace(String),
|
||||||
PrefixIdentifier(String),
|
PrefixIdentifier(String),
|
||||||
}
|
}
|
||||||
|
@ -66,6 +67,30 @@ pub(crate) fn retrieve_field_attribute(name: &str, input: &syn::Field) -> Option
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn retrieve_attr(name: &str, attributes: &Vec<syn::Attribute>) -> Option<bool> {
|
||||||
|
for attr in attributes {
|
||||||
|
if !attr.path.is_ident(XML) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nested = match attr.parse_meta() {
|
||||||
|
Ok(Meta::List(meta)) => meta.nested,
|
||||||
|
_ => return Some(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = match nested.first() {
|
||||||
|
Some(NestedMeta::Meta(Meta::Path(path))) => path,
|
||||||
|
_ => return Some(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.get_ident()? == name {
|
||||||
|
return Some(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn retrieve_attr_list(name: &str, attributes: &Vec<syn::Attribute>) -> Option<syn::MetaList> {
|
fn retrieve_attr_list(name: &str, attributes: &Vec<syn::Attribute>) -> Option<syn::MetaList> {
|
||||||
for attr in attributes {
|
for attr in attributes {
|
||||||
if !attr.path.is_ident(XML) {
|
if !attr.path.is_ident(XML) {
|
||||||
|
@ -74,8 +99,7 @@ fn retrieve_attr_list(name: &str, attributes: &Vec<syn::Attribute>) -> Option<sy
|
||||||
|
|
||||||
let nested = match attr.parse_meta() {
|
let nested = match attr.parse_meta() {
|
||||||
Ok(Meta::List(meta)) => meta.nested,
|
Ok(Meta::List(meta)) => meta.nested,
|
||||||
Ok(_) => todo!(),
|
_ => return None,
|
||||||
_ => todo!(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let list = match nested.first() {
|
let list = match nested.first() {
|
||||||
|
@ -134,7 +158,7 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
// Check if prefix exist
|
// Check if prefix exist
|
||||||
#(
|
#(
|
||||||
if serializer.parent_prefixes.get(#missing_prefixes).is_none() {
|
if serializer.parent_prefixes.get(#missing_prefixes).is_none() {
|
||||||
return Err(instant_xml::Error::WrongPrefix);
|
return Err(instant_xml::Error::UnexpectedPrefix);
|
||||||
}
|
}
|
||||||
)*;
|
)*;
|
||||||
|
|
||||||
|
@ -159,18 +183,13 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
|
||||||
#[proc_macro_derive(FromXml, attributes(xml))]
|
#[proc_macro_derive(FromXml, attributes(xml))]
|
||||||
pub fn from_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn from_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let ast = parse_macro_input!(input as syn::ItemStruct);
|
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||||
let ident = &ast.ident;
|
let ident = &ast.ident;
|
||||||
let name = ident.to_string();
|
|
||||||
|
let deserializer = de::Deserializer::new(&ast);
|
||||||
proc_macro::TokenStream::from(quote!(
|
proc_macro::TokenStream::from(quote!(
|
||||||
impl<'xml> FromXml<'xml> for #ident {
|
impl<'xml> FromXml<'xml> for #ident {
|
||||||
fn from_xml(input: &str) -> Result<Self, ::instant_xml::Error> {
|
#deserializer
|
||||||
use ::instant_xml::parse::Parse;
|
|
||||||
let mut iter = ::instant_xml::xmlparser::Tokenizer::from(input);
|
|
||||||
iter.next().element_start(None, #name)?;
|
|
||||||
iter.next().element_end(None, #name)?;
|
|
||||||
Ok(Self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::{Deserializer, EntityType, Error, FromXml, TagName, Visitor};
|
||||||
|
|
||||||
|
struct BoolVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for BoolVisitor {
|
||||||
|
type Value = bool;
|
||||||
|
|
||||||
|
fn visit_str<'a>(self, value: &str) -> Result<Self::Value, Error> {
|
||||||
|
match FromStr::from_str(value) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => Err(Error::Other(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for bool {
|
||||||
|
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error> {
|
||||||
|
match deserializer.consume_next_type() {
|
||||||
|
EntityType::Element => deserializer.deserialize_bool(BoolVisitor),
|
||||||
|
EntityType::Attribute => deserializer.deserialize_attribute(BoolVisitor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,29 @@ use thiserror::Error;
|
||||||
pub use xmlparser;
|
pub use xmlparser;
|
||||||
|
|
||||||
pub use macros::{FromXml, ToXml};
|
pub use macros::{FromXml, ToXml};
|
||||||
|
use parse::XmlParser;
|
||||||
|
|
||||||
|
pub mod impls;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
|
|
||||||
|
pub struct TagData<'xml> {
|
||||||
|
pub key: &'xml str,
|
||||||
|
pub attributes: Vec<(&'xml str, &'xml str)>,
|
||||||
|
|
||||||
|
// TODO: handle default namespace
|
||||||
|
pub default_namespace: Option<&'xml str>,
|
||||||
|
|
||||||
|
pub namespaces: Option<HashMap<&'xml str, &'xml str>>,
|
||||||
|
pub prefix: Option<&'xml str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum XmlRecord<'xml> {
|
||||||
|
Open(TagData<'xml>),
|
||||||
|
Element(&'xml str),
|
||||||
|
Close(&'xml str),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ToXml {
|
pub trait ToXml {
|
||||||
fn to_xml(&self) -> Result<String, Error> {
|
fn to_xml(&self) -> Result<String, Error> {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
@ -181,8 +200,167 @@ pub struct FieldContext<'xml> {
|
||||||
pub attribute: Option<FieldAttribute<'xml>>,
|
pub attribute: Option<FieldAttribute<'xml>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub enum EntityType {
|
||||||
|
Element,
|
||||||
|
Attribute,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TagName<'xml> {
|
||||||
|
FieldName,
|
||||||
|
Custom(&'xml str),
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FromXml<'xml>: Sized {
|
pub trait FromXml<'xml>: Sized {
|
||||||
fn from_xml(input: &str) -> Result<Self, Error>;
|
const TAG_NAME: TagName<'xml>;
|
||||||
|
|
||||||
|
fn from_xml(input: &str) -> Result<Self, Error> {
|
||||||
|
let mut deserializer = Deserializer::new(input);
|
||||||
|
Self::deserialize(&mut deserializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Visitor<'xml>: Sized {
|
||||||
|
type Value;
|
||||||
|
|
||||||
|
fn visit_str(self, _value: &str) -> Result<Self::Value, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_struct<'a>(&self, _deserializer: &'a mut Deserializer) -> Result<Self::Value, Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Deserializer<'xml> {
|
||||||
|
parser: XmlParser<'xml>,
|
||||||
|
namespaces: HashMap<&'xml str, &'xml str>,
|
||||||
|
tag_attributes: Vec<(&'xml str, &'xml str)>,
|
||||||
|
next_type: EntityType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> Deserializer<'xml> {
|
||||||
|
pub fn new(input: &'xml str) -> Self {
|
||||||
|
Self {
|
||||||
|
parser: XmlParser::new(input),
|
||||||
|
namespaces: std::collections::HashMap::new(),
|
||||||
|
tag_attributes: Vec::new(),
|
||||||
|
next_type: EntityType::Element,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_next_tag(&mut self) -> Result<Option<XmlRecord>, Error> {
|
||||||
|
self.parser.peek_next_tag()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_namespace(&self, namespace_to_verify: &str) -> bool {
|
||||||
|
self.namespaces.get(namespace_to_verify).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_next_attribute(&self) -> Option<&(&'xml str, &'xml str)> {
|
||||||
|
self.tag_attributes.last()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_struct<V>(
|
||||||
|
&mut self,
|
||||||
|
visitor: V,
|
||||||
|
name: &str,
|
||||||
|
namespaces: &HashMap<&'xml str, &'xml str>,
|
||||||
|
) -> Result<V::Value, Error>
|
||||||
|
where
|
||||||
|
V: Visitor<'xml>,
|
||||||
|
{
|
||||||
|
let new_namespaces = namespaces
|
||||||
|
.iter()
|
||||||
|
.filter(|(k, v)| self.namespaces.insert(k, v).is_none())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
self.process_open_tag(name, namespaces)?;
|
||||||
|
let ret = visitor.visit_struct(self)?;
|
||||||
|
|
||||||
|
self.check_close_tag(name)?;
|
||||||
|
let _ = new_namespaces
|
||||||
|
.iter()
|
||||||
|
.map(|(k, _)| self.namespaces.remove(*k));
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_next_type_as_attribute(&mut self) -> Result<(), Error> {
|
||||||
|
if self.next_type == EntityType::Attribute {
|
||||||
|
return Err(Error::UnexpectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.next_type = EntityType::Attribute;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume_next_type(&mut self) -> EntityType {
|
||||||
|
let ret = self.next_type.clone();
|
||||||
|
self.next_type = EntityType::Element;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_bool<V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
||||||
|
where
|
||||||
|
V: Visitor<'xml>,
|
||||||
|
{
|
||||||
|
self.parser.next();
|
||||||
|
match self.parser.next() {
|
||||||
|
Some(Ok(XmlRecord::Element(v))) => {
|
||||||
|
let ret = visitor.visit_str(v);
|
||||||
|
self.parser.next();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
_ => Err(Error::UnexpectedValue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_attribute<V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
||||||
|
where
|
||||||
|
V: Visitor<'xml>,
|
||||||
|
{
|
||||||
|
match self.tag_attributes.pop() {
|
||||||
|
Some((_, value)) => visitor.visit_str(value),
|
||||||
|
None => Err(Error::UnexpectedEndOfStream),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_open_tag(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
namespaces: &HashMap<&'xml str, &'xml str>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let item = match self.parser.next() {
|
||||||
|
Some(Ok(XmlRecord::Open(item))) if item.key == name => item,
|
||||||
|
_ => return Err(Error::UnexpectedValue),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (k, v) in item.namespaces.unwrap() {
|
||||||
|
match namespaces.get(k) {
|
||||||
|
Some(item) if *item != v => return Err(Error::UnexpectedPrefix),
|
||||||
|
None => return Err(Error::MissingdPrefix),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tag_attributes = item.attributes;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_close_tag(&mut self, name: &str) -> Result<(), Error> {
|
||||||
|
let item = match self.parser.next() {
|
||||||
|
Some(item) => item?,
|
||||||
|
None => return Err(Error::MissingTag),
|
||||||
|
};
|
||||||
|
|
||||||
|
match item {
|
||||||
|
XmlRecord::Close(v) if v == name => Ok(()),
|
||||||
|
_ => Err(Error::UnexpectedTag),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromXmlOwned: for<'xml> FromXml<'xml> {}
|
pub trait FromXmlOwned: for<'xml> FromXml<'xml> {}
|
||||||
|
@ -198,10 +376,24 @@ pub enum Error {
|
||||||
Format(#[from] fmt::Error),
|
Format(#[from] fmt::Error),
|
||||||
#[error("parse: {0}")]
|
#[error("parse: {0}")]
|
||||||
Parse(#[from] xmlparser::Error),
|
Parse(#[from] xmlparser::Error),
|
||||||
|
#[error("other: {0}")]
|
||||||
|
Other(std::string::String),
|
||||||
#[error("unexpected end of stream")]
|
#[error("unexpected end of stream")]
|
||||||
UnexpectedEndOfStream,
|
UnexpectedEndOfStream,
|
||||||
#[error("unexpected value")]
|
#[error("unexpected value")]
|
||||||
UnexpectedValue,
|
UnexpectedValue,
|
||||||
#[error("wrong prefix")]
|
#[error("unexpected tag")]
|
||||||
WrongPrefix,
|
UnexpectedTag,
|
||||||
|
#[error("missing tag")]
|
||||||
|
MissingTag,
|
||||||
|
#[error("missing value")]
|
||||||
|
MissingValue,
|
||||||
|
#[error("unexpected token")]
|
||||||
|
UnexpectedToken,
|
||||||
|
#[error("missing prefix")]
|
||||||
|
MissingdPrefix,
|
||||||
|
#[error("unexpected prefix")]
|
||||||
|
UnexpectedPrefix,
|
||||||
|
#[error("unexpected state")]
|
||||||
|
UnexpectedState,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,134 @@
|
||||||
use xmlparser::{ElementEnd, Token};
|
use std::collections::HashMap;
|
||||||
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use super::Error;
|
use xmlparser::{ElementEnd, Token, Tokenizer};
|
||||||
|
|
||||||
impl<'a> Parse for Option<Result<xmlparser::Token<'a>, xmlparser::Error>> {
|
use crate::Error;
|
||||||
fn element_start(self, ns: Option<&str>, tag: &str) -> Result<(), Error> {
|
pub use crate::{TagData, XmlRecord};
|
||||||
match self {
|
|
||||||
Some(Ok(Token::ElementStart { prefix, local, .. })) => {
|
pub struct XmlParser<'xml> {
|
||||||
let prefix_ns = prefix.as_str();
|
stack: Vec<&'xml str>,
|
||||||
let (has_prefix, expect_prefix) = (!prefix_ns.is_empty(), ns.is_some());
|
iter: Peekable<Tokenizer<'xml>>,
|
||||||
if has_prefix != expect_prefix {
|
|
||||||
return dbg!(Err(Error::UnexpectedValue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_prefix && Some(prefix_ns) != ns {
|
impl<'a> XmlParser<'a> {
|
||||||
return dbg!(Err(Error::UnexpectedValue));
|
pub fn new(input: &'a str) -> XmlParser<'a> {
|
||||||
}
|
XmlParser {
|
||||||
|
stack: Vec::new(),
|
||||||
if local.as_str() != tag {
|
iter: Tokenizer::from(input).peekable(),
|
||||||
return dbg!(Err(Error::UnexpectedValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Some(Ok(_)) => Err(Error::UnexpectedValue),
|
|
||||||
Some(Err(err)) => Err(err.into()),
|
|
||||||
None => Err(Error::UnexpectedEndOfStream),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn element_end(self, _: Option<&str>, _: &str) -> Result<(), Error> {
|
pub fn peek_next_tag(&mut self) -> Result<Option<XmlRecord>, Error> {
|
||||||
match self {
|
let item = match self.iter.peek() {
|
||||||
Some(Ok(Token::ElementEnd { end, .. })) => match end {
|
Some(v) => v,
|
||||||
ElementEnd::Open => todo!(),
|
None => return Ok(None),
|
||||||
ElementEnd::Close(_, _) => todo!(),
|
};
|
||||||
ElementEnd::Empty => Ok(()),
|
|
||||||
|
match item {
|
||||||
|
Ok(Token::ElementStart { prefix, local, .. }) => {
|
||||||
|
let prefix = match prefix.is_empty() {
|
||||||
|
true => None,
|
||||||
|
false => Some(prefix.as_str()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(XmlRecord::Open(TagData {
|
||||||
|
key: local,
|
||||||
|
attributes: Vec::new(),
|
||||||
|
default_namespace: None,
|
||||||
|
namespaces: None,
|
||||||
|
prefix,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
Ok(Token::ElementEnd {
|
||||||
|
end: ElementEnd::Close(..),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if self.stack.is_empty() {
|
||||||
|
return Err(Error::UnexpectedEndOfStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Some(XmlRecord::Close(self.stack.last().unwrap())));
|
||||||
|
}
|
||||||
|
Ok(_) => Err(Error::UnexpectedToken),
|
||||||
|
Err(e) => Err(Error::Parse(*e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> Iterator for XmlParser<'xml> {
|
||||||
|
type Item = Result<XmlRecord<'xml>, Error>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mut key: Option<&str> = None;
|
||||||
|
let mut prefix_ret: Option<&str> = None;
|
||||||
|
let mut default_namespace = None;
|
||||||
|
let mut namespaces = HashMap::new();
|
||||||
|
let mut attributes = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let item = match self.iter.next() {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match item {
|
||||||
|
Ok(Token::ElementStart { prefix, local, .. }) => {
|
||||||
|
key = Some(local.as_str());
|
||||||
|
prefix_ret = match prefix.is_empty() {
|
||||||
|
true => None,
|
||||||
|
false => Some(prefix.as_str()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(Token::ElementEnd { end, .. }) => match end {
|
||||||
|
ElementEnd::Open => {
|
||||||
|
self.stack.push(key.unwrap());
|
||||||
|
|
||||||
|
return Some(Ok(XmlRecord::Open(TagData {
|
||||||
|
key: key.unwrap(),
|
||||||
|
attributes,
|
||||||
|
default_namespace,
|
||||||
|
namespaces: Some(namespaces),
|
||||||
|
prefix: prefix_ret,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
ElementEnd::Close(_, v) => match self.stack.pop() {
|
||||||
|
Some(last) if last == v.as_str() => {
|
||||||
|
return Some(Ok(XmlRecord::Close(last)));
|
||||||
|
}
|
||||||
|
_ => return Some(Err(Error::UnexpectedValue)),
|
||||||
},
|
},
|
||||||
Some(Ok(_)) => Err(Error::UnexpectedValue),
|
ElementEnd::Empty => {
|
||||||
Some(Err(err)) => Err(err.into()),
|
todo!();
|
||||||
None => Err(Error::UnexpectedEndOfStream),
|
}
|
||||||
|
},
|
||||||
|
Ok(Token::Attribute {
|
||||||
|
prefix,
|
||||||
|
local,
|
||||||
|
value,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if prefix.is_empty() && local.as_str() == "xmlns" {
|
||||||
|
// Default namespace
|
||||||
|
default_namespace = Some(value.as_str());
|
||||||
|
} 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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Token::Text { text }) => {
|
||||||
|
return Some(Ok(XmlRecord::Element(text.as_str())));
|
||||||
|
}
|
||||||
|
Ok(_) => return Some(Err(Error::UnexpectedToken)),
|
||||||
|
Err(e) => return Some(Err(Error::Parse(e))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Parse {
|
|
||||||
fn element_start(self, ns: Option<&str>, tag: &str) -> Result<(), Error>;
|
|
||||||
fn element_end(self, ns: Option<&str>, tag: &str) -> Result<(), Error>;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,26 @@
|
||||||
use instant_xml::{FromXml, ToXml};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
#[derive(Debug, Eq, PartialEq, ToXml, FromXml)]
|
||||||
struct Unit;
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
|
||||||
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
|
||||||
struct StructWithCustomField {
|
|
||||||
test: Nested,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
|
||||||
struct Nested {
|
struct Nested {
|
||||||
#[xml(namespace(bar))]
|
#[xml(namespace(bar))]
|
||||||
flag: bool,
|
flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
|
||||||
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
|
||||||
struct StructWithCustomFieldWrongPrefix {
|
|
||||||
test: NestedWrongPrefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
struct NestedWrongPrefix {
|
struct NestedWrongPrefix {
|
||||||
#[xml(namespace(dar))]
|
#[xml(namespace(dar))]
|
||||||
flag: bool,
|
flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
|
struct Unit;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unit() {
|
||||||
|
assert_eq!(Unit.to_xml().unwrap(), "<Unit></Unit>");
|
||||||
|
//assert_eq!(Unit::from_xml("<Unit/>").unwrap(), Unit);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
||||||
struct StructWithNamedFields {
|
struct StructWithNamedFields {
|
||||||
|
@ -37,12 +31,6 @@ struct StructWithNamedFields {
|
||||||
number: i32,
|
number: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unit() {
|
|
||||||
assert_eq!(Unit.to_xml().unwrap(), "<Unit></Unit>");
|
|
||||||
assert_eq!(Unit::from_xml("<Unit/>").unwrap(), Unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn struct_with_named_fields() {
|
fn struct_with_named_fields() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -57,6 +45,12 @@ fn struct_with_named_fields() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
|
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
||||||
|
struct StructWithCustomField {
|
||||||
|
test: Nested,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn struct_with_custom_field() {
|
fn struct_with_custom_field() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -72,6 +66,12 @@ fn struct_with_custom_field() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
|
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
||||||
|
struct StructWithCustomFieldWrongPrefix {
|
||||||
|
test: NestedWrongPrefix,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn struct_with_custom_field_wrong_prefix() {
|
fn struct_with_custom_field_wrong_prefix() {
|
||||||
|
@ -81,3 +81,39 @@ fn struct_with_custom_field_wrong_prefix() {
|
||||||
.to_xml()
|
.to_xml()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, FromXml)]
|
||||||
|
#[xml(namespace("URI", bar = "BAZ", foo = "BAR"))]
|
||||||
|
struct StructWithCustomFieldFromXml {
|
||||||
|
#[xml(namespace(bar))]
|
||||||
|
flag: bool,
|
||||||
|
#[xml(attribute)]
|
||||||
|
flag_attribute: bool,
|
||||||
|
test: Nested,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn struct_with_custom_field_from_xml() {
|
||||||
|
assert_eq!(
|
||||||
|
StructWithCustomFieldFromXml::from_xml("<StructWithCustomFieldFromXml flag_attribute=\"true\" xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><bar:flag>false</bar:flag><Nested><flag>true</flag></Nested></StructWithCustomFieldFromXml>").unwrap(),
|
||||||
|
StructWithCustomFieldFromXml {
|
||||||
|
flag: false,
|
||||||
|
flag_attribute: true,
|
||||||
|
test: Nested { flag: true }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Different order
|
||||||
|
assert_eq!(
|
||||||
|
StructWithCustomFieldFromXml::from_xml("<StructWithCustomFieldFromXml xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\" flag_attribute=\"true\"><Nested><flag>true</flag></Nested><flag>false</flag></StructWithCustomFieldFromXml>").unwrap(),
|
||||||
|
StructWithCustomFieldFromXml {
|
||||||
|
flag: false,
|
||||||
|
flag_attribute: true,
|
||||||
|
test: Nested { flag: true }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Nested::from_xml("<Nested><flag>true</flag></Nested>").unwrap(),
|
||||||
|
Nested { flag: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue