serialization derive
This commit is contained in:
parent
a2757b00ea
commit
0747fece03
|
@ -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"
|
||||
|
|
|
@ -45,4 +45,4 @@ path = "pbtc/main.rs"
|
|||
name = "pbtc"
|
||||
|
||||
[workspace]
|
||||
members = ["bencher"]
|
||||
members = ["bencher", "serialization_derive"]
|
||||
|
|
|
@ -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" }
|
|
@ -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()?, },
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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); },
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue