Serializer scalars (#15)
This commit is contained in:
parent
2a9901bc84
commit
c553b22310
|
@ -6,7 +6,7 @@ mod se;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
use syn::{parse_macro_input, Lit, Meta, NestedMeta};
|
use syn::{parse_macro_input, Lit, Meta, NestedMeta};
|
||||||
|
|
||||||
use crate::se::Serializer;
|
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 {
|
pub fn to_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 ident = &ast.ident;
|
||||||
|
let generics = (&ast.generics).into_token_stream();
|
||||||
|
|
||||||
let root_name = ident.to_string();
|
let root_name = ident.to_string();
|
||||||
let mut serializer = Serializer::new(&ast.attrs);
|
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();
|
let current_namespaces = serializer.namespaces_token();
|
||||||
|
|
||||||
proc_macro::TokenStream::from(quote!(
|
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>
|
fn serialize<W>(&self, serializer: &mut instant_xml::Serializer<W>) -> Result<(), instant_xml::Error>
|
||||||
where
|
where
|
||||||
W: std::fmt::Write,
|
W: std::fmt::Write,
|
||||||
|
|
|
@ -91,11 +91,6 @@ impl<'a> Serializer {
|
||||||
Some(FieldAttribute::Namespace(namespace)) => {
|
Some(FieldAttribute::Namespace(namespace)) => {
|
||||||
body.extend(quote!(
|
body.extend(quote!(
|
||||||
#declaration
|
#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));
|
field.attribute = Some(instant_xml::FieldAttribute::Namespace(#namespace));
|
||||||
));
|
));
|
||||||
body
|
body
|
||||||
|
@ -125,7 +120,7 @@ impl<'a> Serializer {
|
||||||
attributes.extend(quote!(
|
attributes.extend(quote!(
|
||||||
#declaration
|
#declaration
|
||||||
|
|
||||||
serializer.add_attribute_key(&#name);
|
serializer.add_attribute_key(&#name)?;
|
||||||
field.attribute = Some(instant_xml::FieldAttribute::Attribute);
|
field.attribute = Some(instant_xml::FieldAttribute::Attribute);
|
||||||
));
|
));
|
||||||
attributes
|
attributes
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
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;
|
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::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
pub use xmlparser;
|
pub use xmlparser;
|
||||||
|
@ -38,96 +39,6 @@ pub trait ToXml {
|
||||||
W: fmt::Write;
|
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>
|
pub struct Serializer<'xml, W>
|
||||||
where
|
where
|
||||||
W: fmt::Write,
|
W: fmt::Write,
|
||||||
|
@ -163,16 +74,24 @@ impl<'xml, W: std::fmt::Write> Serializer<'xml, W> {
|
||||||
Ok(())
|
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(' ');
|
||||||
self.current_attributes.push_str(attr_key);
|
write!(self.current_attributes, "{}", attr_key)?;
|
||||||
self.current_attributes.push('=');
|
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('"');
|
||||||
self.current_attributes.push_str(attr_value);
|
write!(self.current_attributes, "{}", attr_value)?;
|
||||||
self.current_attributes.push('"');
|
self.current_attributes.push('"');
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_field_context(&mut self, field_context: FieldContext<'xml>) -> Result<(), Error> {
|
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};
|
use instant_xml::{Error, FromXml, ToXml};
|
||||||
|
|
||||||
//TODO: Add compile time errors check?
|
//TODO: Add compile time errors check?
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
|
@ -377,3 +380,81 @@ fn direct_namespaces() {
|
||||||
Error::WrongNamespace
|
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