Serializer scalars (#15)
This commit is contained in:
parent
2a9901bc84
commit
c553b22310
|
@ -6,7 +6,7 @@ mod se;
|
|||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{parse_macro_input, Lit, Meta, NestedMeta};
|
||||
|
||||
use crate::se::Serializer;
|
||||
|
@ -104,6 +104,8 @@ fn retrieve_attr_list(attributes: &Vec<syn::Attribute>) -> Option<(Option<syn::M
|
|||
pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
let ident = &ast.ident;
|
||||
let generics = (&ast.generics).into_token_stream();
|
||||
|
||||
let root_name = ident.to_string();
|
||||
let mut serializer = Serializer::new(&ast.attrs);
|
||||
|
||||
|
@ -133,7 +135,7 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
let current_namespaces = serializer.namespaces_token();
|
||||
|
||||
proc_macro::TokenStream::from(quote!(
|
||||
impl ToXml for #ident {
|
||||
impl #generics ToXml for #ident #generics {
|
||||
fn serialize<W>(&self, serializer: &mut instant_xml::Serializer<W>) -> Result<(), instant_xml::Error>
|
||||
where
|
||||
W: std::fmt::Write,
|
||||
|
|
|
@ -91,11 +91,6 @@ impl<'a> Serializer {
|
|||
Some(FieldAttribute::Namespace(namespace)) => {
|
||||
body.extend(quote!(
|
||||
#declaration
|
||||
// // Check if such namespace already exist, if so change it to use its prefix
|
||||
// match serializer.parent_namespaces.get(#namespace) {
|
||||
// Some(key) => field.attribute = Some(instant_xml::FieldAttribute::Prefix(key)),
|
||||
// None => field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace)),
|
||||
// };
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace));
|
||||
));
|
||||
body
|
||||
|
@ -125,7 +120,7 @@ impl<'a> Serializer {
|
|||
attributes.extend(quote!(
|
||||
#declaration
|
||||
|
||||
serializer.add_attribute_key(&#name);
|
||||
serializer.add_attribute_key(&#name)?;
|
||||
field.attribute = Some(instant_xml::FieldAttribute::Attribute);
|
||||
));
|
||||
attributes
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{Deserializer, EntityType, Error, FromXml, TagName, Visitor};
|
||||
use crate::{
|
||||
Deserializer, EntityType, Error, FieldAttribute, FromXml, Serializer, TagName, ToXml, Visitor,
|
||||
};
|
||||
|
||||
struct BoolVisitor;
|
||||
|
||||
|
@ -25,3 +29,150 @@ impl<'xml> FromXml<'xml> for bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serializer
|
||||
struct DisplayToXml<'a, T: fmt::Display>(pub &'a T);
|
||||
|
||||
impl<'a, T> ToXml for DisplayToXml<'a, T>
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
let field_context = match serializer.consume_field_context() {
|
||||
Some(field_context) => field_context,
|
||||
None => return Err(Error::UnexpectedValue),
|
||||
};
|
||||
|
||||
match field_context.attribute {
|
||||
Some(FieldAttribute::Attribute) => {
|
||||
serializer.add_attribute_value(&self.0)?;
|
||||
}
|
||||
_ => {
|
||||
serializer.add_open_tag(&field_context)?;
|
||||
write!(serializer.output, "{}", self.0)?;
|
||||
serializer.add_close_tag(field_context)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! to_xml_for_number {
|
||||
($typ:ty) => {
|
||||
impl ToXml for $typ {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
DisplayToXml(self).serialize(serializer)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
to_xml_for_number!(i8);
|
||||
to_xml_for_number!(i16);
|
||||
to_xml_for_number!(i32);
|
||||
to_xml_for_number!(i64);
|
||||
to_xml_for_number!(isize);
|
||||
to_xml_for_number!(u8);
|
||||
to_xml_for_number!(u16);
|
||||
to_xml_for_number!(u32);
|
||||
to_xml_for_number!(u64);
|
||||
to_xml_for_number!(usize);
|
||||
to_xml_for_number!(f32);
|
||||
to_xml_for_number!(f64);
|
||||
|
||||
impl ToXml for bool {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
let value = match self {
|
||||
true => "true",
|
||||
false => "false",
|
||||
};
|
||||
|
||||
DisplayToXml(&value).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for String {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
DisplayToXml(&escape(self)?).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for char {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
let mut tmp = [0u8; 4];
|
||||
DisplayToXml(&escape(&*self.encode_utf8(&mut tmp))?).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for &str {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
DisplayToXml(&escape(self)?).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for Cow<'_, str> {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
DisplayToXml(&escape(self)?).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToXml for Option<T>
|
||||
where
|
||||
T: ToXml,
|
||||
{
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
match self {
|
||||
Some(v) => v.serialize(serializer),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn escape(input: &str) -> Result<Cow<'_, str>, Error> {
|
||||
let mut result = String::with_capacity(input.len());
|
||||
let mut last_end = 0;
|
||||
for (start, c) in input.chars().enumerate() {
|
||||
let to = match c {
|
||||
'&' => "&",
|
||||
'"' => """,
|
||||
'<' => "<",
|
||||
'>' => ">",
|
||||
'\'' => "'",
|
||||
_ => continue,
|
||||
};
|
||||
result.push_str(input.get(last_end..start).unwrap());
|
||||
result.push_str(to);
|
||||
last_end = start + 1;
|
||||
}
|
||||
|
||||
if result.is_empty() {
|
||||
return Ok(Cow::Borrowed(input));
|
||||
}
|
||||
|
||||
result.push_str(input.get(last_end..input.len()).unwrap());
|
||||
Ok(Cow::Owned(result))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
|
||||
use thiserror::Error;
|
||||
pub use xmlparser;
|
||||
|
@ -38,96 +39,6 @@ pub trait ToXml {
|
|||
W: fmt::Write;
|
||||
}
|
||||
|
||||
macro_rules! to_xml_for_number {
|
||||
($typ:ty) => {
|
||||
impl ToXml for $typ {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
match serializer.consume_field_context() {
|
||||
Some(field_context) => {
|
||||
match field_context.attribute {
|
||||
Some(FieldAttribute::Attribute) => {
|
||||
serializer.add_attribute_value(&self.to_string());
|
||||
}
|
||||
_ => {
|
||||
serializer.add_open_tag(&field_context)?;
|
||||
write!(serializer.output, "{}", &self)?;
|
||||
serializer.add_close_tag(field_context)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::UnexpectedValue),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
to_xml_for_number!(i8);
|
||||
to_xml_for_number!(i16);
|
||||
to_xml_for_number!(i32);
|
||||
to_xml_for_number!(i64);
|
||||
to_xml_for_number!(u8);
|
||||
to_xml_for_number!(u16);
|
||||
to_xml_for_number!(u32);
|
||||
to_xml_for_number!(u64);
|
||||
|
||||
impl ToXml for bool {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
let value = match self {
|
||||
true => "true",
|
||||
false => "false",
|
||||
};
|
||||
|
||||
match serializer.consume_field_context() {
|
||||
Some(field_context) => {
|
||||
match field_context.attribute {
|
||||
Some(FieldAttribute::Attribute) => {
|
||||
serializer.add_attribute_value(value);
|
||||
}
|
||||
_ => {
|
||||
serializer.add_open_tag(&field_context)?;
|
||||
serializer.output.write_str(value)?;
|
||||
serializer.add_close_tag(field_context)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::UnexpectedValue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for String {
|
||||
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
|
||||
where
|
||||
W: fmt::Write,
|
||||
{
|
||||
match serializer.consume_field_context() {
|
||||
Some(field_context) => {
|
||||
match field_context.attribute {
|
||||
Some(FieldAttribute::Attribute) => {
|
||||
serializer.add_attribute_value(self);
|
||||
}
|
||||
_ => {
|
||||
serializer.add_open_tag(&field_context)?;
|
||||
serializer.output.write_str(self)?;
|
||||
serializer.add_close_tag(field_context)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::UnexpectedValue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Serializer<'xml, W>
|
||||
where
|
||||
W: fmt::Write,
|
||||
|
@ -163,16 +74,24 @@ impl<'xml, W: std::fmt::Write> Serializer<'xml, W> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_attribute_key(&mut self, attr_key: &str) {
|
||||
pub fn add_attribute_key<T>(&mut self, attr_key: &T) -> Result<(), Error>
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
self.current_attributes.push(' ');
|
||||
self.current_attributes.push_str(attr_key);
|
||||
write!(self.current_attributes, "{}", attr_key)?;
|
||||
self.current_attributes.push('=');
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_attribute_value(&mut self, attr_value: &str) {
|
||||
pub fn add_attribute_value<T>(&mut self, attr_value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
self.current_attributes.push('"');
|
||||
self.current_attributes.push_str(attr_value);
|
||||
write!(self.current_attributes, "{}", attr_value)?;
|
||||
self.current_attributes.push('"');
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_field_context(&mut self, field_context: FieldContext<'xml>) -> Result<(), Error> {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use instant_xml::{Error, FromXml, ToXml};
|
||||
|
||||
//TODO: Add compile time errors check?
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||
|
@ -377,3 +380,81 @@ fn direct_namespaces() {
|
|||
Error::WrongNamespace
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, ToXml)]
|
||||
#[xml(namespace("URI"))]
|
||||
struct StructDeserializerScalars<'a, 'b> {
|
||||
bool_type: bool,
|
||||
i8_type: i8,
|
||||
u32_type: u32,
|
||||
string_type: String,
|
||||
str_type_a: &'a str,
|
||||
str_type_b: &'b str,
|
||||
char_type: char,
|
||||
f32_type: f32,
|
||||
cow: Cow<'a, str>,
|
||||
option: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalars() {
|
||||
// Option some
|
||||
assert_eq!(
|
||||
StructDeserializerScalars{
|
||||
bool_type: true,
|
||||
i8_type: 1,
|
||||
u32_type: 42,
|
||||
string_type: "string".to_string(),
|
||||
str_type_a: "lifetime a",
|
||||
str_type_b: "lifetime b",
|
||||
char_type: 'c',
|
||||
f32_type: 1.20,
|
||||
cow: Cow::from("123"),
|
||||
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><option>asd</option></StructDeserializerScalars>"
|
||||
);
|
||||
|
||||
// Option none
|
||||
assert_eq!(
|
||||
StructDeserializerScalars{
|
||||
bool_type: true,
|
||||
i8_type: 1,
|
||||
u32_type: 42,
|
||||
string_type: "string".to_string(),
|
||||
str_type_a: "lifetime a",
|
||||
str_type_b: "lifetime b",
|
||||
char_type: 'c',
|
||||
f32_type: 1.20,
|
||||
cow: Cow::from("123"),
|
||||
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></StructDeserializerScalars>"
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, ToXml)]
|
||||
#[xml(namespace("URI"))]
|
||||
struct StructSpecialEntities<'a> {
|
||||
string_type: String,
|
||||
str_type_a: &'a str,
|
||||
cow: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_entities() {
|
||||
assert_eq!(
|
||||
StructSpecialEntities{
|
||||
string_type: "&\"<>\'aa".to_string(),
|
||||
str_type_a: "&\"<>\'bb",
|
||||
cow: Cow::from("&\"<>\'cc"),
|
||||
}
|
||||
.to_xml()
|
||||
.unwrap(),
|
||||
"<StructSpecialEntities xmlns=\"URI\"><string_type>&"<>'aa</string_type><str_type_a>&"<>'bb</str_type_a><cow>&"<>'cc</cow></StructSpecialEntities>"
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue