initial commit

This commit is contained in:
Marc-Antoine Arnaud 2018-04-09 19:04:11 +02:00
parent f948bbcd29
commit d002cabe95
10 changed files with 730 additions and 0 deletions

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[workspace]
members = [
"yaserde",
"yaserde_derive",
]

12
yaserde/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "yaserde"
version = "0.1.0"
authors = ["Marc-Antoine Arnaud <arnaud.marcantoine@gmail.com>"]
[dependencies]
yaserde_derive = { path = "../yaserde_derive", optional = true }
regex = "0.2"
xml-rs = "0.7.0"
[dev-dependencies]
yaserde_derive = { path = "../yaserde_derive" }

18
yaserde/src/lib.rs Normal file
View File

@ -0,0 +1,18 @@
extern crate xml;
#[cfg(feature = "yaserde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate yaserde_derive;
use std::io::Read;
use xml::EventReader;
use xml::attribute::OwnedAttribute;
pub trait YaDeserialize : Sized {
fn derive_deserialize<R: Read>(read: &mut EventReader<R>, parent_attributes: Option<&Vec<OwnedAttribute>>) -> Result<Self, String>;
}
pub trait YaSerialize {
fn derive_serialize();
}

View File

@ -0,0 +1,163 @@
extern crate yaserde;
#[macro_use]
extern crate yaserde_derive;
extern crate xml;
use std::io::Read;
use xml::reader::EventReader;
use yaserde::{YaDeserialize, YaSerialize};
#[test]
fn test_basic() {
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="base")]
pub struct XmlStruct {
item: String
}
let content = "<base><item>something</item></base>".to_string();
let mut parser = EventReader::from_str(content.as_str());
let loaded = XmlStruct::derive_deserialize(&mut parser, None);
assert_eq!(loaded, Ok(XmlStruct{
item: "something".to_string()
}));
}
#[test]
fn test_list_of_items() {
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="base")]
pub struct XmlStruct {
items: Vec<String>
}
let content = "<base><items>something1</items><items>something2</items></base>".to_string();
let mut parser = EventReader::from_str(content.as_str());
let loaded = XmlStruct::derive_deserialize(&mut parser, None);
assert_eq!(loaded, Ok(XmlStruct{
items: vec![
"something1".to_string(),
"something2".to_string()
]
}));
}
#[test]
fn test_attributes() {
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="base")]
pub struct XmlStruct {
#[yaserde(attribute)]
item: String,
sub: SubStruct
}
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="sub")]
pub struct SubStruct {
#[yaserde(attribute)]
subitem: String
}
impl Default for SubStruct {
fn default() -> SubStruct {
SubStruct{
subitem: "".to_string()
}
}
}
let content = "<base item=\"something\"><sub subitem=\"sub-something\"></sub></base>".to_string();
let mut parser = EventReader::from_str(content.as_str());
let loaded = XmlStruct::derive_deserialize(&mut parser, None);
assert_eq!(loaded, Ok(XmlStruct{
item: "something".to_string(),
sub: SubStruct{
subitem: "sub-something".to_string()
}
}));
}
#[test]
fn test_rename() {
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="base")]
pub struct XmlStruct {
#[yaserde(attribute, rename="Item")]
item: String,
#[yaserde(rename="sub")]
sub_struct: SubStruct
}
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="sub")]
pub struct SubStruct {
#[yaserde(attribute, rename="sub_item")]
subitem: String,
}
impl Default for SubStruct {
fn default() -> SubStruct {
SubStruct{
subitem: "".to_string()
}
}
}
let content = "<base Item=\"something\"><sub sub_item=\"sub_something\"></sub></base>".to_string();
let mut parser = EventReader::from_str(content.as_str());
let loaded = XmlStruct::derive_deserialize(&mut parser, None);
assert_eq!(loaded, Ok(XmlStruct{
item: "something".to_string(),
sub_struct: SubStruct{
subitem: "sub_something".to_string()
}
}));
}
#[test]
fn test_text_content_with_attributes() {
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="base")]
pub struct XmlStruct {
#[yaserde(attribute, rename="Item")]
item: String,
#[yaserde(rename="sub")]
sub_struct: SubStruct
}
#[derive(YaDeserialize, YaSerialize, PartialEq, Debug)]
#[yaserde(root="sub")]
pub struct SubStruct {
#[yaserde(attribute, rename="sub_item")]
subitem: String,
#[yaserde(text)]
text: String
}
impl Default for SubStruct {
fn default() -> SubStruct {
SubStruct{
subitem: "".to_string(),
text: "".to_string(),
}
}
}
let content = "<base Item=\"something\"><sub sub_item=\"sub_something\">text_content</sub></base>".to_string();
let mut parser = EventReader::from_str(content.as_str());
let loaded = XmlStruct::derive_deserialize(&mut parser, None);
assert_eq!(loaded, Ok(XmlStruct{
item: "something".to_string(),
sub_struct: SubStruct{
subitem: "sub_something".to_string(),
text: "text_content".to_string()
}
}));
}

13
yaserde_derive/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "yaserde_derive"
version = "0.1.0"
authors = ["Marc-Antoine Arnaud <arnaud.marcantoine@gmail.com>"]
[dependencies]
syn = { version = "0.12.14", features = ["visit", "extra-traits"] }
proc-macro2 = "0.2.3"
quote = "0.4.2"
[lib]
name = "yaserde_derive"
proc-macro = true

View File

@ -0,0 +1,83 @@
use proc_macro2::TokenNode::*;
use proc_macro2::Delimiter::Parenthesis;
use syn::Attribute;
#[derive(Debug, Clone)]
pub struct YaSerdeAttribute {
pub root: Option<String>,
pub rename: Option<String>,
pub attribute: bool,
pub text: bool,
}
impl YaSerdeAttribute {
pub fn parse(attrs: &Vec<Attribute>) -> YaSerdeAttribute {
let mut root = None;
let mut rename = None;
let mut attribute = false;
let mut text = false;
for attr in attrs.iter() {
let mut attr_iter = attr.clone().tts.into_iter();
match attr_iter.next() {
Some(token) => {
match token.kind {
Group(Parenthesis, token_stream) => {
let mut attr_iter = token_stream.into_iter();
while let Some(item) = attr_iter.next() {
match item.kind {
Term(t) => {
match t.as_str() {
"root" => {
attr_iter.next();
let v = attr_iter.next().map(|s|
match s.kind {
Literal(l) => {
Some(l.to_string().replace("\"", ""))
},
_ => None
});
root = v.unwrap_or(None);
},
"rename" => {
attr_iter.next();
let v = attr_iter.next().map(|s|
match s.kind {
Literal(l) => {
Some(l.to_string().replace("\"", ""))
},
_ => None
});
rename = v.unwrap_or(None);
},
"attribute" => {
attribute = true;
}
"text" => {
text = true;
}
_ => {},
}
},
_ => {}
}
}
},
_ => {},
}
},
None => {},
}
}
YaSerdeAttribute {
root: root,
rename: rename,
attribute: attribute,
text: text,
}
}
}

View File

@ -0,0 +1,285 @@
use der::attribute::*;
use der::field_type::*;
use quote::Tokens;
use syn::Ident;
use syn::DataStruct;
use syn::punctuated::Pair;
use syn::Type::Path;
use proc_macro2::Span;
pub fn parse(data_struct: &DataStruct, name: &Ident, root: &String, _root_attributes: &YaSerdeAttribute) -> Tokens {
let variables : Tokens = data_struct.fields.iter().map(|ref field|
{
let label = field.ident;
match field.ty {
Path(ref path) => {
match path.path.segments.first() {
Some(Pair::End(t)) => {
let pair = path.path.segments.first().unwrap();
match t.ident.to_string().as_str() {
"String" => {
Some(quote!{
let mut #label : #pair = "".to_string();
})
},
"Vec" => {
Some(quote!{
let mut #label : #pair = vec![];
})
},
_ => {
Some(quote!{
let mut #label : #pair = #pair::default();
})
},
}
},
_ => {
None
},
}
},
_ => {None},
}
})
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.fold(Tokens::new(), |mut sum, val| {sum.append_all(val); sum});
let attributes_loading: Tokens = data_struct.fields.iter().map(|ref field|
match get_field_type(field) {
Some(FieldType::FieldTypeString) => {
let label = field.ident;
let field_attrs = YaSerdeAttribute::parse(&field.attrs);
match (field_attrs.attribute, field_attrs.rename) {
(true, Some(value)) => {
let label_name = Ident::new(&format!("{}", value), Span::call_site()).to_string();
Some(quote!{
match current_attributes {
Some(attributes) =>
for attr in attributes {
if attr.name.local_name == #label_name {
#label = attr.value.to_owned();
}
},
None => {},
}
})
},
(true, None) => {
let label_name = field.ident.unwrap().to_string();
Some(quote!{
match current_attributes {
Some(attributes) =>
for attr in attributes {
if attr.name.local_name == #label_name {
#label = attr.value.to_owned();
}
},
None => {},
}
})
}
_ => None
}
}
_ => {
None
}
})
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.fold(Tokens::new(), |mut sum, val| {sum.append_all(val); sum});
let assign_text_field: Tokens = data_struct.fields.iter().map(|ref field|
match get_field_type(field) {
Some(FieldType::FieldTypeString) => {
let label = field.ident;
let field_attrs = YaSerdeAttribute::parse(&field.attrs);
match field_attrs.text {
true => {
Some(quote!{
#label = characters_content.to_owned();
})
},
false => None
}
}
_ => {
None
}
})
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.fold(Tokens::new(), |mut sum, val| {sum.append_all(val); sum});
let fields : Tokens = data_struct.fields.iter().map(|ref field|
{
let field_attrs = YaSerdeAttribute::parse(&field.attrs);
let label = field.ident;
let renamed_label =
match field_attrs.rename {
Some(value) => Some(Ident::new(&format!("{}", value), Span::call_site())),
None => field.ident
};
let label_name = renamed_label.unwrap().to_string();
match get_field_type(field) {
Some(FieldType::FieldTypeString) => {
Some(quote!{
#label_name => {
match read.next() {
Ok(xml::reader::XmlEvent::Characters(characters_content)) => {
#label = characters_content.trim().to_string();
},
_ => {},
}
},
})
},
Some(FieldType::FieldTypeStruct{name}) => {
let struct_ident = Ident::new(&format!("{}", name), Span::def_site());
Some(quote!{
#label_name => {
match #struct_ident::derive_deserialize(read, Some(&attributes)) {
Ok(parsed_structure) => {
prev_level -= 1;
#label = parsed_structure;
},
Err(msg) => {
println!("ERROR {:?}", msg);
},
}
},
})
},
Some(FieldType::FieldTypeVec) => {
match get_vec_type(field) {
Some(identifier) => {
match identifier.to_string().as_str() {
"String" => {
Some(quote!{
#label_name => {
match read.next() {
Ok(xml::reader::XmlEvent::Characters(characters_content)) => {
#label.push(characters_content.trim().to_string());
},
_ => {},
}
},
})
},
struct_name => {
let struct_ident = Ident::new(&format!("{}", struct_name), Span::def_site());
Some(quote!{
#label_name => {
match #struct_ident::derive_deserialize(read, Some(&attributes)) {
Ok(parsed_item) => {
prev_level -= 1;
#label.push(parsed_item);
},
Err(msg) => {
println!("ERROR {:?}", msg);
},
}
},
})
}
}
},
None => None
}
},
_ => None
}
})
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.fold(Tokens::new(), |mut sum, val| {sum.append_all(val); sum});
let struct_builder : Tokens = data_struct.fields.iter().map(|ref field|
{
let label = field.ident;
match get_field_type(field) {
Some(FieldType::FieldTypeString) |
Some(FieldType::FieldTypeStruct{..}) |
Some(FieldType::FieldTypeVec) =>
Some(quote!{
#label: #label,
}),
None => None,
}
})
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.fold(Tokens::new(), |mut tokens, token| {tokens.append_all(token); tokens});
quote! {
use xml::reader::XmlEvent;
impl YaDeserialize for #name {
#[allow(unused_variables)]
fn derive_deserialize<R: Read>(read: &mut xml::EventReader<R>, parent_attributes: Option<&Vec<xml::attribute::OwnedAttribute>>) -> Result<Self, String> {
let mut prev_level = 0;
let mut current_level = 0;
#variables
let current_attributes = parent_attributes;
#attributes_loading
loop {
match read.next() {
Ok(XmlEvent::StartDocument{..}) => {
},
Ok(XmlEvent::EndDocument) => {
break;
},
Ok(XmlEvent::StartElement{name, attributes, namespace: _namespace}) => {
// println!("{} | {} - {}: {}", #root, prev_level, current_level, name.local_name.as_str());
if prev_level == current_level {
match name.local_name.as_str() {
#root => {
let root_attributes = attributes.clone();
let current_attributes = Some(&root_attributes);
#attributes_loading
current_level += 1;
},
#fields
_ => {}
};
}
prev_level += 1;
},
Ok(XmlEvent::EndElement{name}) => {
if #root == name.local_name.as_str() {
// println!("BREAK {}", #root);
break;
}
prev_level -= 1;
}
Ok(xml::reader::XmlEvent::Characters(characters_content)) => {
if prev_level == current_level {
#assign_text_field
}
},
Ok(_event) => {
},
Err(_msg) => {
break;
},
}
}
Ok(#name{#struct_builder})
}
}
}
}

View File

@ -0,0 +1,70 @@
use syn;
use syn::punctuated::Pair;
use syn::Type::Path;
#[derive(Debug)]
pub enum FieldType {
FieldTypeString,
FieldTypeVec,
FieldTypeStruct{name: String},
}
pub fn get_field_type(field: &syn::Field) -> Option<FieldType> {
match field.ty {
Path(ref path) => {
match path.path.segments.first() {
Some(Pair::End(t)) => {
match t.ident.to_string().as_str() {
"String" => Some(FieldType::FieldTypeString),
"Vec" => Some(FieldType::FieldTypeVec),
name => Some(FieldType::FieldTypeStruct{name: name.to_string()}),
}
},
_ => {
None
},
}
},
_ => {None},
}
}
pub fn get_vec_type(field: &syn::Field) -> Option<syn::Ident> {
match field.ty {
Path(ref path) => {
match path.path.segments.first() {
Some(Pair::End(t)) => {
match t.arguments {
syn::PathArguments::AngleBracketed(ref args) => {
match args.args.first() {
Some(Pair::End(tt)) => {
match tt {
&syn::GenericArgument::Type(ref argument) => {
match argument {
&Path(ref path2) => {
match path2.path.segments.first() {
Some(Pair::End(ttt)) => {
Some(ttt.ident)
},
_ => None
}
},
_ => None
}
},
_ => None
}
},
_ => None
}
},
_ => None
}
},
_ => None
}
}
_ => None
}
}

View File

@ -0,0 +1,43 @@
pub mod attribute;
pub mod expand_struct;
pub mod field_type;
use proc_macro2::Span;
use quote;
use syn;
use syn::Ident;
pub fn expand_derive_deserialize(ast: &syn::DeriveInput) -> Result<quote::Tokens, String> {
let name = &ast.ident;
let attrs = &ast.attrs;
let data = &ast.data;
let root_attrs = attribute::YaSerdeAttribute::parse(&attrs);
let root = root_attrs.clone().root.unwrap_or(name.to_string());
let impl_block =
match data {
&syn::Data::Struct(ref data_struct) => {
expand_struct::parse(data_struct, &name, &root, &root_attrs)
},
&syn::Data::Enum(ref _data_enum) => {
unimplemented!()
},
&syn::Data::Union(ref _data_union) => {
unimplemented!()
},
};
let dummy_const = Ident::new(&format!("_IMPL_DESERIALIZE_FOR_{}", name), Span::def_site());
let generated = quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
extern crate yaserde as _yaserde;
#impl_block
};
};
Ok(generated)
}

38
yaserde_derive/src/lib.rs Normal file
View File

@ -0,0 +1,38 @@
#![recursion_limit="128"]
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;
mod der;
use proc_macro::TokenStream;
fn expand_derive_serialize(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
quote! {
impl YaSerialize for #name {
fn derive_serialize() {
println!("serialize {}", stringify!(#name));
}
}
}
}
#[proc_macro_derive(YaDeserialize, attributes(yaserde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
match der::expand_derive_deserialize(&ast) {
Ok(expanded) => expanded.into(),
Err(msg) => panic!(msg),
}
}
#[proc_macro_derive(YaSerialize)]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = expand_derive_serialize(&ast);
gen.into()
}