Deserialize scalars (#14)
Co-authored-by: Dirkjan Ochtman <dirkjan@ochtman.nl>
This commit is contained in:
parent
c553b22310
commit
ebd913f603
|
@ -1,5 +1,5 @@
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
use crate::{get_namespaces, retrieve_field_attribute, FieldAttribute};
|
use crate::{get_namespaces, retrieve_field_attribute, FieldAttribute};
|
||||||
|
|
||||||
|
@ -34,6 +34,25 @@ impl quote::ToTokens for Deserializer {
|
||||||
impl Deserializer {
|
impl Deserializer {
|
||||||
pub fn new(input: &syn::DeriveInput) -> Deserializer {
|
pub fn new(input: &syn::DeriveInput) -> Deserializer {
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
|
let generics = (&input.generics).into_token_stream();
|
||||||
|
let lifetimes = (&input.generics.params).into_token_stream();
|
||||||
|
|
||||||
|
let mut lifetime_xml = TokenStream::new();
|
||||||
|
let mut lifetime_visitor = TokenStream::new();
|
||||||
|
let iter = &mut input.generics.params.iter();
|
||||||
|
if let Some(it) = iter.next() {
|
||||||
|
lifetime_xml = quote!(:);
|
||||||
|
lifetime_xml.extend(it.into_token_stream());
|
||||||
|
while let Some(it) = iter.by_ref().next() {
|
||||||
|
lifetime_xml.extend(quote!(+));
|
||||||
|
lifetime_xml.extend(it.into_token_stream());
|
||||||
|
}
|
||||||
|
lifetime_xml.extend(quote!(,));
|
||||||
|
lifetime_xml.extend(lifetimes.clone());
|
||||||
|
lifetime_visitor.extend(quote!(,));
|
||||||
|
lifetime_visitor.extend(lifetimes);
|
||||||
|
}
|
||||||
|
|
||||||
let name = ident.to_string();
|
let name = ident.to_string();
|
||||||
let mut out = TokenStream::new();
|
let mut out = TokenStream::new();
|
||||||
|
|
||||||
|
@ -113,7 +132,7 @@ impl Deserializer {
|
||||||
let attr_type_match = attributes_tokens.match_;
|
let attr_type_match = attributes_tokens.match_;
|
||||||
|
|
||||||
out.extend(quote!(
|
out.extend(quote!(
|
||||||
fn deserialize(deserializer: &mut ::instant_xml::Deserializer) -> Result<Self, ::instant_xml::Error> {
|
fn deserialize(deserializer: &mut ::instant_xml::Deserializer<'xml>) -> Result<Self, ::instant_xml::Error> {
|
||||||
use ::instant_xml::parse::XmlRecord;
|
use ::instant_xml::parse::XmlRecord;
|
||||||
use ::instant_xml::{Error, Deserializer, Visitor} ;
|
use ::instant_xml::{Error, Deserializer, Visitor} ;
|
||||||
|
|
||||||
|
@ -143,12 +162,18 @@ impl Deserializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StructVisitor;
|
struct StructVisitor<'xml #lifetime_xml> {
|
||||||
impl<'xml> Visitor<'xml> for StructVisitor {
|
marker: std::marker::PhantomData<#ident #generics>,
|
||||||
type Value = #ident;
|
lifetime: std::marker::PhantomData<&'xml ()>,
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_struct<'a>(&self, deserializer: &mut ::instant_xml::Deserializer) -> Result<Self::Value, ::instant_xml::Error>
|
impl<'xml #lifetime_xml> Visitor<'xml> for StructVisitor<'xml #lifetime_visitor> {
|
||||||
{
|
type Value = #ident #generics;
|
||||||
|
|
||||||
|
fn visit_struct(
|
||||||
|
&self,
|
||||||
|
deserializer: &mut ::instant_xml::Deserializer<'xml>
|
||||||
|
) -> Result<Self::Value, ::instant_xml::Error> {
|
||||||
#declare_values
|
#declare_values
|
||||||
while let Some(( key, _ )) = deserializer.peek_next_attribute() {
|
while let Some(( key, _ )) = deserializer.peek_next_attribute() {
|
||||||
match get_attribute(&key) {
|
match get_attribute(&key) {
|
||||||
|
@ -181,7 +206,15 @@ impl Deserializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#namespaces_map;
|
#namespaces_map;
|
||||||
deserializer.deserialize_struct(StructVisitor{}, #name, #default_namespace, &namespaces_map)
|
deserializer.deserialize_struct(
|
||||||
|
StructVisitor{
|
||||||
|
marker: std::marker::PhantomData,
|
||||||
|
lifetime: std::marker::PhantomData
|
||||||
|
},
|
||||||
|
#name,
|
||||||
|
#default_namespace,
|
||||||
|
&namespaces_map
|
||||||
|
)
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -189,6 +222,12 @@ impl Deserializer {
|
||||||
const TAG_NAME: ::instant_xml::TagName<'xml> = ::instant_xml::TagName::Custom(#name);
|
const TAG_NAME: ::instant_xml::TagName<'xml> = ::instant_xml::TagName::Custom(#name);
|
||||||
));
|
));
|
||||||
|
|
||||||
|
out = quote!(
|
||||||
|
impl<'xml #lifetime_xml> FromXml<'xml> for #ident #generics {
|
||||||
|
#out
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Deserializer { out }
|
Deserializer { out }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,16 +245,14 @@ impl Deserializer {
|
||||||
let field_var = field.ident.as_ref().unwrap();
|
let field_var = field.ident.as_ref().unwrap();
|
||||||
let field_var_str = field_var.to_string();
|
let field_var_str = field_var.to_string();
|
||||||
let const_field_var_str = Ident::new(&field_var_str.to_uppercase(), Span::call_site());
|
let const_field_var_str = Ident::new(&field_var_str.to_uppercase(), Span::call_site());
|
||||||
let field_type = match &field.ty {
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
syn::Type::Path(v) => v.path.get_ident(),
|
discard_lifetimes(&mut no_lifetime_type);
|
||||||
_ => panic!("Wrong field attribute format"),
|
|
||||||
};
|
|
||||||
|
|
||||||
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,));
|
||||||
|
|
||||||
tokens.consts.extend(quote!(
|
tokens.consts.extend(quote!(
|
||||||
const #const_field_var_str: &str = match #field_type::TAG_NAME {
|
const #const_field_var_str: &str = match <#no_lifetime_type>::TAG_NAME {
|
||||||
::instant_xml::TagName::FieldName => #field_var_str,
|
::instant_xml::TagName::FieldName => #field_var_str,
|
||||||
::instant_xml::TagName::Custom(v) => v,
|
::instant_xml::TagName::Custom(v) => v,
|
||||||
};
|
};
|
||||||
|
@ -232,7 +269,7 @@ impl Deserializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_values.extend(quote!(
|
declare_values.extend(quote!(
|
||||||
let mut #enum_name: Option<#field_type> = None;
|
let mut #enum_name: Option<#no_lifetime_type> = None;
|
||||||
));
|
));
|
||||||
|
|
||||||
let def_prefix = match def_prefix {
|
let def_prefix = match def_prefix {
|
||||||
|
@ -283,7 +320,7 @@ impl Deserializer {
|
||||||
}
|
}
|
||||||
#field_namespace
|
#field_namespace
|
||||||
deserializer.set_next_def_namespace(field_namespace)?;
|
deserializer.set_next_def_namespace(field_namespace)?;
|
||||||
#enum_name = Some(#field_type::deserialize(deserializer)?);
|
#enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
@ -294,13 +331,53 @@ impl Deserializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializer.set_next_type_as_attribute()?;
|
deserializer.set_next_type_as_attribute()?;
|
||||||
#enum_name = Some(#field_type::deserialize(deserializer)?);
|
#enum_name = Some(<#no_lifetime_type>::deserialize(deserializer)?);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return_val.extend(quote!(
|
return_val.extend(quote!(
|
||||||
#field_var: #enum_name.expect("Expected some value"),
|
#field_var: match #enum_name {
|
||||||
|
Some(v) => v,
|
||||||
|
None => <#no_lifetime_type>::missing_value()?,
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn discard_lifetimes(ty: &mut syn::Type) {
|
||||||
|
match ty {
|
||||||
|
syn::Type::Path(ty) => discard_path_lifetimes(ty),
|
||||||
|
syn::Type::Reference(ty) => {
|
||||||
|
ty.lifetime = None;
|
||||||
|
discard_lifetimes(&mut ty.elem);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_path_lifetimes(path: &mut syn::TypePath) {
|
||||||
|
if let Some(q) = &mut path.qself {
|
||||||
|
discard_lifetimes(&mut q.ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
for segment in &mut path.path.segments {
|
||||||
|
match &mut segment.arguments {
|
||||||
|
syn::PathArguments::None => {}
|
||||||
|
syn::PathArguments::AngleBracketed(args) => {
|
||||||
|
args.args.iter_mut().for_each(|arg| match arg {
|
||||||
|
syn::GenericArgument::Lifetime(lt) => {
|
||||||
|
*lt = syn::Lifetime::new("'_", Span::call_site())
|
||||||
|
}
|
||||||
|
syn::GenericArgument::Type(ty) => discard_lifetimes(ty),
|
||||||
|
syn::GenericArgument::Binding(_)
|
||||||
|
| syn::GenericArgument::Constraint(_)
|
||||||
|
| syn::GenericArgument::Const(_) => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
syn::PathArguments::Parenthesized(args) => {
|
||||||
|
args.inputs.iter_mut().for_each(discard_lifetimes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -168,12 +168,9 @@ 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::DeriveInput);
|
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||||
let ident = &ast.ident;
|
|
||||||
|
|
||||||
let deserializer = de::Deserializer::new(&ast);
|
let deserializer = de::Deserializer::new(&ast);
|
||||||
|
|
||||||
proc_macro::TokenStream::from(quote!(
|
proc_macro::TokenStream::from(quote!(
|
||||||
impl<'xml> FromXml<'xml> for #ident {
|
|
||||||
#deserializer
|
#deserializer
|
||||||
}
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Deserializer, EntityType, Error, FieldAttribute, FromXml, Serializer, TagName, ToXml, Visitor,
|
Deserializer, EntityType, Error, FieldAttribute, FromXml, Serializer, TagName, ToXml, Visitor,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BoolVisitor;
|
// Deserializer
|
||||||
|
struct FromStrToVisitor<T: FromStr>(PhantomData<T>)
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: std::fmt::Display;
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for BoolVisitor {
|
impl<'xml, T> Visitor<'xml> for FromStrToVisitor<T>
|
||||||
type Value = bool;
|
where
|
||||||
|
T: FromStr,
|
||||||
fn visit_str<'a>(self, value: &str) -> Result<Self::Value, Error> {
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
fn visit_str(self, value: &str) -> Result<Self::Value, Error> {
|
||||||
match FromStr::from_str(value) {
|
match FromStr::from_str(value) {
|
||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
Err(e) => Err(Error::Other(e.to_string())),
|
Err(e) => Err(Error::Other(e.to_string())),
|
||||||
|
@ -19,12 +27,22 @@ impl<'de> Visitor<'de> for BoolVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BoolVisitor;
|
||||||
|
|
||||||
|
impl<'xml> Visitor<'xml> for BoolVisitor {
|
||||||
|
type Value = bool;
|
||||||
|
|
||||||
|
fn visit_str(self, value: &str) -> Result<Self::Value, Error> {
|
||||||
|
FromStrToVisitor(PhantomData::<Self::Value>).visit_str(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'xml> FromXml<'xml> for bool {
|
impl<'xml> FromXml<'xml> for bool {
|
||||||
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
||||||
|
|
||||||
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error> {
|
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error> {
|
||||||
match deserializer.consume_next_type() {
|
match deserializer.consume_next_type() {
|
||||||
EntityType::Element => deserializer.deserialize_bool(BoolVisitor),
|
EntityType::Element => deserializer.deserialize_element(BoolVisitor),
|
||||||
EntityType::Attribute => deserializer.deserialize_attribute(BoolVisitor),
|
EntityType::Attribute => deserializer.deserialize_attribute(BoolVisitor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +91,223 @@ macro_rules! to_xml_for_number {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NumberVisitor<T>
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml, T> Visitor<'xml> for NumberVisitor<T>
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
|
||||||
|
fn visit_str(self, value: &str) -> Result<Self::Value, Error> {
|
||||||
|
FromStrToVisitor(PhantomData::<Self::Value>).visit_str(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! from_xml_for_number {
|
||||||
|
($typ:ty) => {
|
||||||
|
impl<'xml> FromXml<'xml> for $typ {
|
||||||
|
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error> {
|
||||||
|
match deserializer.consume_next_type() {
|
||||||
|
EntityType::Element => deserializer.deserialize_element(NumberVisitor {
|
||||||
|
marker: PhantomData,
|
||||||
|
}),
|
||||||
|
EntityType::Attribute => deserializer.deserialize_attribute(NumberVisitor {
|
||||||
|
marker: PhantomData,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
from_xml_for_number!(i8);
|
||||||
|
from_xml_for_number!(i16);
|
||||||
|
from_xml_for_number!(i32);
|
||||||
|
from_xml_for_number!(i64);
|
||||||
|
from_xml_for_number!(isize);
|
||||||
|
from_xml_for_number!(u8);
|
||||||
|
from_xml_for_number!(u16);
|
||||||
|
from_xml_for_number!(u32);
|
||||||
|
from_xml_for_number!(u64);
|
||||||
|
from_xml_for_number!(usize);
|
||||||
|
from_xml_for_number!(f32);
|
||||||
|
from_xml_for_number!(f64);
|
||||||
|
|
||||||
|
struct StringVisitor;
|
||||||
|
|
||||||
|
impl<'xml> Visitor<'xml> for StringVisitor {
|
||||||
|
type Value = String;
|
||||||
|
|
||||||
|
fn visit_str(self, value: &str) -> Result<Self::Value, Error> {
|
||||||
|
Ok(escape_back(value).into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for String {
|
||||||
|
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error> {
|
||||||
|
//<&'xml str>::deserialize(deserializer);
|
||||||
|
match deserializer.consume_next_type() {
|
||||||
|
EntityType::Element => deserializer.deserialize_element(StringVisitor),
|
||||||
|
EntityType::Attribute => deserializer.deserialize_attribute(StringVisitor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CharVisitor;
|
||||||
|
|
||||||
|
impl<'xml> Visitor<'xml> for CharVisitor {
|
||||||
|
type Value = char;
|
||||||
|
|
||||||
|
fn visit_str(self, value: &str) -> Result<Self::Value, Error> {
|
||||||
|
match value.len() {
|
||||||
|
1 => Ok(value.chars().next().expect("char type")),
|
||||||
|
_ => Err(Error::Other("Expected char type".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for char {
|
||||||
|
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error> {
|
||||||
|
match deserializer.consume_next_type() {
|
||||||
|
EntityType::Element => deserializer.deserialize_element(CharVisitor),
|
||||||
|
EntityType::Attribute => deserializer.deserialize_attribute(CharVisitor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StrVisitor;
|
||||||
|
|
||||||
|
impl<'a> Visitor<'a> for StrVisitor {
|
||||||
|
type Value = &'a str;
|
||||||
|
|
||||||
|
fn visit_str(self, value: &'a str) -> Result<Self::Value, Error> {
|
||||||
|
match escape_back(value) {
|
||||||
|
Cow::Owned(v) => Err(Error::Other(format!("Unsupported char: {}", v))),
|
||||||
|
Cow::Borrowed(v) => Ok(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for &'xml str {
|
||||||
|
const TAG_NAME: TagName<'xml> = TagName::FieldName;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer<'xml>) -> Result<Self, Error> {
|
||||||
|
match deserializer.consume_next_type() {
|
||||||
|
EntityType::Element => deserializer.deserialize_element(StrVisitor),
|
||||||
|
EntityType::Attribute => deserializer.deserialize_attribute(StrVisitor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CowStrVisitor;
|
||||||
|
|
||||||
|
impl<'a> Visitor<'a> for CowStrVisitor {
|
||||||
|
type Value = Cow<'a, str>;
|
||||||
|
|
||||||
|
fn visit_str(self, value: &'a str) -> Result<Self::Value, Error> {
|
||||||
|
Ok(escape_back(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for Cow<'xml, str> {
|
||||||
|
const TAG_NAME: TagName<'xml> = <&str>::TAG_NAME;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer<'xml>) -> Result<Self, Error> {
|
||||||
|
match deserializer.consume_next_type() {
|
||||||
|
EntityType::Element => deserializer.deserialize_element(CowStrVisitor),
|
||||||
|
EntityType::Attribute => deserializer.deserialize_attribute(CowStrVisitor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'xml, T> FromXml<'xml> for Option<T>
|
||||||
|
where
|
||||||
|
T: FromXml<'xml>,
|
||||||
|
{
|
||||||
|
const TAG_NAME: TagName<'xml> = <T>::TAG_NAME;
|
||||||
|
|
||||||
|
fn deserialize(deserializer: &mut Deserializer<'xml>) -> Result<Self, Error> {
|
||||||
|
match <T>::deserialize(deserializer) {
|
||||||
|
Ok(v) => Ok(Some(v)),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn missing_value() -> Result<Self, Error> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_back(input: &str) -> Cow<'_, str> {
|
||||||
|
let mut result = String::with_capacity(input.len());
|
||||||
|
let input_len = input.len();
|
||||||
|
|
||||||
|
let mut last_end = 0;
|
||||||
|
while input_len - last_end >= 4 {
|
||||||
|
match &input[last_end..(last_end + 4)] {
|
||||||
|
"<" => {
|
||||||
|
result.push('<');
|
||||||
|
last_end += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
">" => {
|
||||||
|
result.push('>');
|
||||||
|
last_end += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
if input_len - last_end >= 5 {
|
||||||
|
if &input[last_end..(last_end + 5)] == "&" {
|
||||||
|
result.push('&');
|
||||||
|
last_end += 5;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if input_len - last_end >= 6 {
|
||||||
|
match &input[last_end..(last_end + 6)] {
|
||||||
|
"'" => {
|
||||||
|
result.push('\'');
|
||||||
|
last_end += 6;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
""" => {
|
||||||
|
result.push('"');
|
||||||
|
last_end += 6;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str(input.get(last_end..last_end + 1).unwrap());
|
||||||
|
last_end += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str(input.get(last_end..).unwrap());
|
||||||
|
if result.len() == input.len() {
|
||||||
|
return Cow::Borrowed(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cow::Owned(result)
|
||||||
|
}
|
||||||
|
|
||||||
to_xml_for_number!(i8);
|
to_xml_for_number!(i8);
|
||||||
to_xml_for_number!(i16);
|
to_xml_for_number!(i16);
|
||||||
to_xml_for_number!(i32);
|
to_xml_for_number!(i32);
|
||||||
|
|
|
@ -198,22 +198,28 @@ pub enum TagName<'xml> {
|
||||||
pub trait FromXml<'xml>: Sized {
|
pub trait FromXml<'xml>: Sized {
|
||||||
const TAG_NAME: TagName<'xml>;
|
const TAG_NAME: TagName<'xml>;
|
||||||
|
|
||||||
fn from_xml(input: &str) -> Result<Self, Error> {
|
fn from_xml(input: &'xml str) -> Result<Self, Error> {
|
||||||
let mut deserializer = Deserializer::new(input);
|
let mut deserializer = Deserializer::new(input);
|
||||||
Self::deserialize(&mut deserializer)
|
Self::deserialize(&mut deserializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize(deserializer: &mut Deserializer) -> Result<Self, Error>;
|
fn deserialize(deserializer: &mut Deserializer<'xml>) -> Result<Self, Error>;
|
||||||
|
|
||||||
|
// If the missing field is of type `Option<T>` then treat is as `None`,
|
||||||
|
// otherwise it is an error.
|
||||||
|
fn missing_value() -> Result<Self, Error> {
|
||||||
|
Err(Error::MissingValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Visitor<'xml>: Sized {
|
pub trait Visitor<'xml>: Sized {
|
||||||
type Value;
|
type Value;
|
||||||
|
|
||||||
fn visit_str(self, _value: &str) -> Result<Self::Value, Error> {
|
fn visit_str(self, _value: &'xml str) -> Result<Self::Value, Error> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_struct<'a>(&self, _deserializer: &'a mut Deserializer) -> Result<Self::Value, Error> {
|
fn visit_struct(&self, _deserializer: &mut Deserializer<'xml>) -> Result<Self::Value, Error> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,7 +379,7 @@ impl<'xml> Deserializer<'xml> {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_bool<V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
fn deserialize_element<V>(&mut self, visitor: V) -> Result<V::Value, Error>
|
||||||
where
|
where
|
||||||
V: Visitor<'xml>,
|
V: Visitor<'xml>,
|
||||||
{
|
{
|
||||||
|
@ -422,11 +428,6 @@ impl<'xml> Deserializer<'xml> {
|
||||||
|
|
||||||
pub trait FromXmlOwned: for<'xml> FromXml<'xml> {}
|
pub trait FromXmlOwned: for<'xml> FromXml<'xml> {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct State<'a> {
|
|
||||||
prefix: HashMap<&'a str, &'a str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error, PartialEq, Eq)]
|
#[derive(Debug, Error, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("format: {0}")]
|
#[error("format: {0}")]
|
||||||
|
|
|
@ -2,8 +2,6 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use instant_xml::{Error, FromXml, ToXml};
|
use instant_xml::{Error, FromXml, ToXml};
|
||||||
|
|
||||||
//TODO: Add compile time errors check?
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
struct Unit;
|
struct Unit;
|
||||||
|
|
||||||
|
@ -381,7 +379,14 @@ fn direct_namespaces() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, ToXml)]
|
#[derive(Debug, PartialEq, Eq, FromXml, ToXml)]
|
||||||
|
#[xml(namespace("URI"))]
|
||||||
|
struct NestedLifetimes<'a> {
|
||||||
|
flag: bool,
|
||||||
|
str_type_a: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, FromXml, ToXml)]
|
||||||
#[xml(namespace("URI"))]
|
#[xml(namespace("URI"))]
|
||||||
struct StructDeserializerScalars<'a, 'b> {
|
struct StructDeserializerScalars<'a, 'b> {
|
||||||
bool_type: bool,
|
bool_type: bool,
|
||||||
|
@ -392,14 +397,18 @@ struct StructDeserializerScalars<'a, 'b> {
|
||||||
str_type_b: &'b str,
|
str_type_b: &'b str,
|
||||||
char_type: char,
|
char_type: char,
|
||||||
f32_type: f32,
|
f32_type: f32,
|
||||||
|
nested: NestedLifetimes<'a>,
|
||||||
cow: Cow<'a, str>,
|
cow: Cow<'a, str>,
|
||||||
option: Option<&'a str>,
|
option: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scalars() {
|
fn scalars() {
|
||||||
// Option some
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
StructDeserializerScalars::from_xml(
|
||||||
|
"<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.20</f32_type><NestedLifetimes><flag>true</flag><str_type_a>asd</str_type_a></NestedLifetimes><cow>123</cow></StructDeserializerScalars>"
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
StructDeserializerScalars{
|
StructDeserializerScalars{
|
||||||
bool_type: true,
|
bool_type: true,
|
||||||
i8_type: 1,
|
i8_type: 1,
|
||||||
|
@ -409,16 +418,20 @@ fn scalars() {
|
||||||
str_type_b: "lifetime b",
|
str_type_b: "lifetime b",
|
||||||
char_type: 'c',
|
char_type: 'c',
|
||||||
f32_type: 1.20,
|
f32_type: 1.20,
|
||||||
|
nested: NestedLifetimes {
|
||||||
|
flag: true,
|
||||||
|
str_type_a: "asd"
|
||||||
|
},
|
||||||
cow: Cow::from("123"),
|
cow: Cow::from("123"),
|
||||||
option: Some("asd"),
|
option: None,
|
||||||
}
|
}
|
||||||
.to_xml()
|
|
||||||
.unwrap(),
|
|
||||||
"<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.2</f32_type><cow>123</cow><option>asd</option></StructDeserializerScalars>"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Option none
|
// Option none
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
StructDeserializerScalars::from_xml(
|
||||||
|
"<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.2</f32_type><NestedLifetimes><flag>true</flag><str_type_a>asd</str_type_a></NestedLifetimes><cow>123</cow><option>asd</option></StructDeserializerScalars>"
|
||||||
|
).unwrap(),
|
||||||
StructDeserializerScalars{
|
StructDeserializerScalars{
|
||||||
bool_type: true,
|
bool_type: true,
|
||||||
i8_type: 1,
|
i8_type: 1,
|
||||||
|
@ -428,33 +441,78 @@ fn scalars() {
|
||||||
str_type_b: "lifetime b",
|
str_type_b: "lifetime b",
|
||||||
char_type: 'c',
|
char_type: 'c',
|
||||||
f32_type: 1.20,
|
f32_type: 1.20,
|
||||||
|
nested: NestedLifetimes {
|
||||||
|
flag: true,
|
||||||
|
str_type_a: "asd"
|
||||||
|
},
|
||||||
cow: Cow::from("123"),
|
cow: Cow::from("123"),
|
||||||
option: None,
|
option: Some("asd"),
|
||||||
}
|
}
|
||||||
.to_xml()
|
|
||||||
.unwrap(),
|
|
||||||
"<StructDeserializerScalars xmlns=\"URI\"><bool_type>true</bool_type><i8_type>1</i8_type><u32_type>42</u32_type><string_type>string</string_type><str_type_a>lifetime a</str_type_a><str_type_b>lifetime b</str_type_b><char_type>c</char_type><f32_type>1.2</f32_type><cow>123</cow></StructDeserializerScalars>"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, ToXml)]
|
#[derive(Debug, PartialEq, Eq, FromXml, ToXml)]
|
||||||
#[xml(namespace("URI"))]
|
#[xml(namespace("URI"))]
|
||||||
struct StructSpecialEntities<'a> {
|
struct StructSpecialEntities<'a> {
|
||||||
string_type: String,
|
string: String,
|
||||||
str_type_a: &'a str,
|
str: &'a str,
|
||||||
cow: Cow<'a, str>,
|
cow: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn escape_back() {
|
||||||
|
assert_eq!(
|
||||||
|
StructSpecialEntities::from_xml(
|
||||||
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str</str><cow>str&</cow></StructSpecialEntities>"
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
StructSpecialEntities {
|
||||||
|
string: String::from("<>&\"'adsad\""),
|
||||||
|
str: "str",
|
||||||
|
cow: Cow::Owned("str&".to_string()),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wrong str char
|
||||||
|
assert_eq!(
|
||||||
|
StructSpecialEntities::from_xml(
|
||||||
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str&</str></StructSpecialEntities>"
|
||||||
|
)
|
||||||
|
.unwrap_err(),
|
||||||
|
Error::Other("Unsupported char: str&".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Borrowed
|
||||||
|
let escape_back = StructSpecialEntities::from_xml(
|
||||||
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str</str><cow>str</cow></StructSpecialEntities>"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Cow::Owned(_) = escape_back.cow {
|
||||||
|
panic!("Should be Borrowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owned
|
||||||
|
let escape_back = StructSpecialEntities::from_xml(
|
||||||
|
"<StructSpecialEntities xmlns=\"URI\"><string><>&"'adsad"</string><str>str</str><cow>str&</cow></StructSpecialEntities>"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Cow::Borrowed(_) = escape_back.cow {
|
||||||
|
panic!("Should be Owned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn special_entities() {
|
fn special_entities() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
StructSpecialEntities{
|
StructSpecialEntities{
|
||||||
string_type: "&\"<>\'aa".to_string(),
|
string: "&\"<>\'aa".to_string(),
|
||||||
str_type_a: "&\"<>\'bb",
|
str: "&\"<>\'bb",
|
||||||
cow: Cow::from("&\"<>\'cc"),
|
cow: Cow::from("&\"<>\'cc"),
|
||||||
}
|
}
|
||||||
.to_xml()
|
.to_xml()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"<StructSpecialEntities xmlns=\"URI\"><string_type>&"<>'aa</string_type><str_type_a>&"<>'bb</str_type_a><cow>&"<>'cc</cow></StructSpecialEntities>"
|
"<StructSpecialEntities xmlns=\"URI\"><string>&"<>'aa</string><str>&"<>'bb</str><cow>&"<>'cc</cow></StructSpecialEntities>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue