serialization derive

This commit is contained in:
debris 2017-07-22 02:17:23 +02:00
parent a2757b00ea
commit 0747fece03
7 changed files with 257 additions and 1 deletions

9
Cargo.lock generated
View File

@ -950,6 +950,15 @@ dependencies = [
"primitives 0.1.0",
]
[[package]]
name = "serialization_derive"
version = "0.1.0"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serialization 0.1.0",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "shell32-sys"
version = "0.1.1"

View File

@ -45,4 +45,4 @@ path = "pbtc/main.rs"
name = "pbtc"
[workspace]
members = ["bencher"]
members = ["bencher", "serialization_derive"]

View File

@ -0,0 +1,13 @@
[package]
name = "serialization_derive"
version = "0.1.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
[lib]
name = "serialization_derive"
proc-macro = true
[dependencies]
syn = "0.11.11"
quote = "0.3.15"
serialization = { path = "../serialization" }

View File

@ -0,0 +1,64 @@
use {syn, quote};
pub fn impl_raw_deserialize(ast: &syn::DeriveInput) -> quote::Tokens {
let body = match ast.body {
syn::Body::Struct(ref s) => s,
_ => panic!("#[derive(RawDeserialize)] is only defined for structs."),
};
let stmts: Vec<_> = match *body {
syn::VariantData::Struct(ref fields) => fields.iter().enumerate().map(deserialize_field_map).collect(),
syn::VariantData::Tuple(ref fields) => fields.iter().enumerate().map(deserialize_field_map).collect(),
syn::VariantData::Unit => panic!("#[derive(RawDeserialize)] is not defined for Unit structs."),
};
let name = &ast.ident;
let dummy_const = syn::Ident::new(format!("_IMPL_RAW_DESERIALIZE_FOR_{}", name));
let impl_block = quote! {
impl serialization::Deserializable for #name {
fn deserialize<T>(reader: &mut serialization::Reader<T>) -> Result<Self, serialization::Error> where T: io::Read {
let result = #name {
#(#stmts)*
};
Ok(result)
}
}
};
quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
extern crate serialization;
use std::io;
#impl_block
};
}
}
fn deserialize_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens {
deserialize_field(tuple.0, tuple.1)
}
fn deserialize_field(index: usize, field: &syn::Field) -> quote::Tokens {
let ident = match field.ident {
Some(ref ident) => ident.to_string(),
None => index.to_string(),
};
let id = syn::Ident::new(ident.to_string());
match field.ty {
syn::Ty::Array(_, _) => quote! { #id: reader.read_list()?, },
syn::Ty::Slice(_) => quote! { #id: reader.read_list()?, },
syn::Ty::Path(_, ref path) => {
let ident = &path.segments.first().expect("there must be at least 1 segment").ident;
match &ident.to_string() as &str {
"Vec" => quote! { #id: reader.read_list()?, },
_ => quote! { #id: reader.read()?, },
}
},
_ => quote! { #id: reader.read()?, },
}
}

View File

@ -0,0 +1,28 @@
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
mod ser;
mod de;
use proc_macro::TokenStream;
use ser::impl_raw_serialize;
use de::impl_raw_deserialize;
#[proc_macro_derive(RawSerialize)]
pub fn raw_serialize(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_raw_serialize(&ast);
gen.parse().unwrap()
}
#[proc_macro_derive(RawDeserialize)]
pub fn raw_deserialize(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_raw_deserialize(&ast);
gen.parse().unwrap()
}

View File

@ -0,0 +1,59 @@
use {syn, quote};
pub fn impl_raw_serialize(ast: &syn::DeriveInput) -> quote::Tokens {
let body = match ast.body {
syn::Body::Struct(ref s) => s,
_ => panic!("#[derive(RawSerialize)] is only defined for structs."),
};
let stmts: Vec<_> = match *body {
syn::VariantData::Struct(ref fields) => fields.iter().enumerate().map(serialize_field_map).collect(),
syn::VariantData::Tuple(ref fields) => fields.iter().enumerate().map(serialize_field_map).collect(),
syn::VariantData::Unit => panic!("#[derive(RawSerialize)] is not defined for Unit structs."),
};
let name = &ast.ident;
let dummy_const = syn::Ident::new(format!("_IMPL_RAW_SERIALIZE_FOR_{}", name));
let impl_block = quote! {
impl serialization::Serializable for #name {
fn serialize(&self, stream: &mut serialization::Stream) {
#(#stmts)*
}
}
};
quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
extern crate serialization;
#impl_block
};
}
}
fn serialize_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens {
serialize_field(tuple.0, tuple.1)
}
fn serialize_field(index: usize, field: &syn::Field) -> quote::Tokens {
let ident = match field.ident {
Some(ref ident) => ident.to_string(),
None => index.to_string(),
};
let id = syn::Ident::new(format!("self.{}", ident));
match field.ty {
syn::Ty::Array(_, _) => quote! { stream.append_list(&#id); },
syn::Ty::Slice(_) => quote! { stream.append_list(#id); },
syn::Ty::Path(_, ref path) => {
let ident = &path.segments.first().expect("there must be at least 1 segment").ident;
match &ident.to_string() as &str {
"Vec" => quote! { stream.append_list(&#id); },
_ => quote! { stream.append(&#id); },
}
},
_ => quote! { stream.append(&#id); },
}
}

View File

@ -0,0 +1,83 @@
extern crate serialization;
#[macro_use]
extern crate serialization_derive;
use serialization::{serialize, deserialize};
#[derive(Debug, PartialEq, RawSerialize, RawDeserialize)]
struct Foo {
a: u8,
b: u16,
c: u32,
d: u64,
}
#[derive(Debug, PartialEq, RawSerialize, RawDeserialize)]
struct Bar {
a: Vec<Foo>,
}
#[test]
fn test_foo_serialize() {
let foo = Foo {
a: 1,
b: 2,
c: 3,
d: 4,
};
let expected = vec![
1u8,
2, 0,
3, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0,
].into();
let result = serialize(&foo);
assert_eq!(result, expected);
let d = deserialize(expected.as_ref()).unwrap();
assert_eq!(foo, d);
}
#[test]
fn test_bar_serialize() {
let foo = Foo {
a: 1,
b: 2,
c: 3,
d: 4,
};
let foo2 = Foo {
a: 5,
b: 6,
c: 7,
d: 8,
};
let expected = vec![
// number of items
2u8,
// first
1,
2, 0,
3, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0,
// second
5,
6, 0,
7, 0, 0, 0,
8, 0, 0, 0, 0, 0, 0, 0,
].into();
let bar = Bar {
a: vec![foo, foo2],
};
let result = serialize(&bar);
assert_eq!(result, expected);
let d = deserialize(expected.as_ref()).unwrap();
assert_eq!(bar, d);
}