Serializer scalars (#15)

This commit is contained in:
choinskib 2022-09-01 09:24:18 +02:00 committed by GitHub
parent 2a9901bc84
commit c553b22310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 251 additions and 103 deletions

View File

@ -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,

View File

@ -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

View File

@ -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 {
'&' => "&amp;",
'"' => "&quot;",
'<' => "&lt;",
'>' => "&gt;",
'\'' => "&apos;",
_ => 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))
}

View File

@ -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> {

View File

@ -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>&amp;&quot;&lt;&gt;&apos;aa</string_type><str_type_a>&amp;&quot;&lt;&gt;&apos;bb</str_type_a><cow>&amp;&quot;&lt;&gt;&apos;cc</cow></StructSpecialEntities>"
);
}