diff --git a/bench_log.rs b/bench_log.rs index f29540d9..5ba68ab7 100644 --- a/bench_log.rs +++ b/bench_log.rs @@ -11,6 +11,7 @@ use ser::Serializable; use ser; #[deriving(Encodable, Decodable)] +#[deriving_serializable] struct Http { protocol: HttpProtocol, status: u32, @@ -23,28 +24,6 @@ struct Http { request_uri: String, } -impl ser::Serializable for Http { - #[inline] - fn serialize< - S: ser::Serializer, - E - >(&self, s: &mut S) -> Result<(), E> { - try!(s.serialize_struct_start("Http", 9)); - - try!(s.serialize_struct_sep("protocol", &self.protocol)); - try!(s.serialize_struct_sep("status", &self.status)); - try!(s.serialize_struct_sep("host_status", &self.host_status)); - try!(s.serialize_struct_sep("up_status", &self.up_status)); - try!(s.serialize_struct_sep("method", &self.method)); - try!(s.serialize_struct_sep("content_type", &self.content_type)); - try!(s.serialize_struct_sep("user_agent", &self.user_agent)); - try!(s.serialize_struct_sep("referer", &self.referer)); - try!(s.serialize_struct_sep("request_uri", &self.request_uri)); - - s.serialize_struct_end() - } -} - #[deriving(Encodable, Decodable)] enum HttpProtocol { HTTP_PROTOCOL_UNKNOWN, @@ -106,6 +85,7 @@ impl ser::Serializable for CacheStatus { } #[deriving(Encodable, Decodable)] +#[deriving_serializable] struct Origin { ip: String, port: u32, @@ -113,23 +93,6 @@ struct Origin { protocol: OriginProtocol, } -impl ser::Serializable for Origin { - #[inline] - fn serialize< - S: ser::Serializer, - E - >(&self, s: &mut S) -> Result<(), E> { - try!(s.serialize_struct_start("Http", 4)); - - try!(s.serialize_struct_sep("ip", &self.ip)); - try!(s.serialize_struct_sep("port", &self.port)); - try!(s.serialize_struct_sep("hostname", &self.hostname)); - try!(s.serialize_struct_sep("protocol", &self.protocol)); - - s.serialize_struct_end() - } -} - #[deriving(Encodable, Decodable)] enum OriginProtocol { ORIGIN_PROTOCOL_UNKNOWN, @@ -437,6 +400,7 @@ impl ser::Serializable for Country { } #[deriving(Encodable, Decodable)] +#[deriving_serializable] struct Log { timestamp: i64, zone_id: u32, @@ -452,31 +416,6 @@ struct Log { ray_id: String, } -impl ser::Serializable for Log { - #[inline] - fn serialize< - S: ser::Serializer, - E - >(&self, s: &mut S) -> Result<(), E> { - try!(s.serialize_struct_start("Log", 12)); - - try!(s.serialize_struct_sep("timestamp", &self.timestamp)); - try!(s.serialize_struct_sep("zone_id", &self.zone_id)); - try!(s.serialize_struct_sep("zone_plan", &self.zone_plan)); - try!(s.serialize_struct_sep("http", &self.http)); - try!(s.serialize_struct_sep("origin", &self.origin)); - try!(s.serialize_struct_sep("country", &self.country)); - try!(s.serialize_struct_sep("cache_status", &self.cache_status)); - try!(s.serialize_struct_sep("server_ip", &self.server_ip)); - try!(s.serialize_struct_sep("server_name", &self.server_name)); - try!(s.serialize_struct_sep("remote_ip", &self.remote_ip)); - try!(s.serialize_struct_sep("bytes_dlv", &self.bytes_dlv)); - try!(s.serialize_struct_sep("ray_id", &self.ray_id)); - - s.serialize_struct_end() - } -} - impl Log { fn new() -> Log { Log { diff --git a/ser.rs b/ser.rs index 659127a7..04a41fd0 100644 --- a/ser.rs +++ b/ser.rs @@ -272,78 +272,30 @@ mod tests { ////////////////////////////////////////////////////////////////////////////// #[deriving(Clone, PartialEq, Show, Decodable)] + #[deriving_serializable] struct Inner { a: (), b: uint, c: HashMap>, } - impl Serializable for Inner { - #[inline] - fn serialize< - S: Serializer, - E - >(&self, s: &mut S) -> Result<(), E> { - try!(s.serialize_struct_start("Inner", 3)); - - try!(s.serialize_struct_sep("a", &self.a)); - try!(s.serialize_struct_sep("b", &self.b)); - try!(s.serialize_struct_sep("c", &self.c)); - - s.serialize_struct_end() - } - } - ////////////////////////////////////////////////////////////////////////////// #[deriving(Clone, PartialEq, Show, Decodable)] + #[deriving_serializable] struct Outer { inner: Vec, } - impl Serializable for Outer { - #[inline] - fn serialize< - S: Serializer, - E - >(&self, s: &mut S) -> Result<(), E> { - try!(s.serialize_struct_start("Outer", 1)); - - try!(s.serialize_struct_sep("inner", &self.inner)); - - s.serialize_struct_end() - } - } - ////////////////////////////////////////////////////////////////////////////// #[deriving(Clone, PartialEq, Show, Decodable)] + #[deriving_serializable] enum Animal { Dog, Frog(String, int) } - impl Serializable for Animal { - #[inline] - fn serialize< - S: Serializer, - E - >(&self, s: &mut S) -> Result<(), E> { - match *self { - Dog => { - try!(s.serialize_enum_start("Animal", "Dog", 0)); - } - Frog(ref x0, ref x1) => { - try!(s.serialize_enum_start("Animal", "Frog", 2)); - - try!(s.serialize_enum_sep(x0)); - try!(s.serialize_enum_sep(x1)); - } - } - s.serialize_enum_end() - } - } - ////////////////////////////////////////////////////////////////////////////// #[deriving(Clone, PartialEq, Show)] diff --git a/serde.rs b/serde.rs index 5afec5cf..f6613c31 100644 --- a/serde.rs +++ b/serde.rs @@ -1,4 +1,6 @@ #![feature(macro_rules, phase)] +#![crate_type = "dylib"] +#![crate_type = "rlib"] // test harness access #[cfg(test)] @@ -7,6 +9,9 @@ extern crate test; #[phase(plugin, link)] extern crate log; +#[phase(plugin)] +extern crate serde_macros; + #[cfg(test)] extern crate debug; @@ -34,3 +39,9 @@ pub mod bench_map; #[cfg(test)] pub mod bench_log; + +// an inner module so we can use serde_macros. +mod serde { + pub use de; + pub use ser; +} diff --git a/serde_macros.rs b/serde_macros.rs new file mode 100644 index 00000000..139ddef8 --- /dev/null +++ b/serde_macros.rs @@ -0,0 +1,179 @@ +#![crate_type = "dylib"] +#![crate_type = "rlib"] + +#![feature(plugin_registrar)] + +extern crate syntax; +extern crate rustc; + +use std::gc::Gc; + +use syntax::ast::{MetaItem, Item, Expr, MutMutable, LitNil}; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, ItemDecorator}; +use syntax::ext::build::AstBuilder; +use syntax::ext::deriving::generic::{MethodDef, EnumMatching, FieldInfo, Struct, Substructure, TraitDef, combine_substructure}; +use syntax::ext::deriving::generic::ty::{Borrowed, LifetimeBounds, Literal, Path, Ptr, Tuple, borrowed_explicit_self}; +use syntax::parse::token; + +use rustc::plugin::Registry; + +#[plugin_registrar] +#[doc(hidden)] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_syntax_extension( + token::intern("deriving_serializable"), + ItemDecorator(derive_serialize)); +} + +fn derive_serialize(cx: &mut ExtCtxt, + sp: Span, + mitem: Gc, + item: Gc, + push: |Gc|) { + let inline = cx.meta_word(sp, token::InternedString::new("inline")); + let attrs = vec!(cx.attribute(sp, inline)); + + let trait_def = TraitDef { + span: sp, + attributes: vec!(), + path: Path::new(vec!("serde", "ser", "Serializable")), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + methods: vec!( + MethodDef { + name: "serialize", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec!(("__S", ast::StaticSize, vec!(Path::new_( + vec!("serde", "ser", "Serializer"), None, + vec!(box Literal(Path::new_local("__E"))), true))), + ("__E", ast::StaticSize, vec!())) + }, + explicit_self: borrowed_explicit_self(), + args: vec!(Ptr(box Literal(Path::new_local("__S")), + Borrowed(None, MutMutable))), + ret_ty: Literal(Path::new_(vec!("std", "result", "Result"), + None, + vec!(box Tuple(Vec::new()), + box Literal(Path::new_local("__E"))), + true)), + attributes: attrs, + const_nonmatching: true, + combine_substructure: combine_substructure(|a, b, c| { + serializable_substructure(a, b, c) + }), + }) + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn serializable_substructure(cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure) -> Gc { + let serializer = substr.nonself_args[0]; + + return match *substr.fields { + Struct(ref fields) => { + if fields.is_empty() { + // unit structs have no fields and need to return `Ok()` + cx.expr_ok(trait_span, cx.expr_lit(trait_span, LitNil)) + } else { + let mut stmts: Vec>> = Vec::new(); + + let call = cx.expr_method_call( + trait_span, + serializer, + cx.ident_of("serialize_struct_start"), + vec!( + cx.expr_str(trait_span, token::get_ident(substr.type_ident)), + cx.expr_uint(trait_span, fields.len()), + ) + ); + let call = cx.expr_try(trait_span, call); + stmts.push(cx.stmt_expr(call)); + + let emit_struct_sep = cx.ident_of("serialize_struct_sep"); + + for (i, &FieldInfo { + name, + self_, + span, + .. + }) in fields.iter().enumerate() { + let name = match name { + Some(id) => token::get_ident(id), + None => { + token::intern_and_get_ident(format!("_field{}", + i).as_slice()) + } + }; + let call = cx.expr_method_call(span, + serializer, + emit_struct_sep, + vec!( + cx.expr_str(span, name), + cx.expr_addr_of(span, self_), + )); + + let call = cx.expr_try(span, call); + stmts.push(cx.stmt_expr(call)); + } + + let call = cx.expr_method_call(trait_span, + serializer, + cx.ident_of("serialize_struct_end"), + vec!()); + + cx.expr_block(cx.block(trait_span, stmts, Some(call))) + } + } + + EnumMatching(_idx, variant, ref fields) => { + let mut stmts = vec!(); + + let call = cx.expr_method_call( + trait_span, + serializer, + cx.ident_of("serialize_enum_start"), + vec!( + cx.expr_str(trait_span, token::get_ident(substr.type_ident)), + cx.expr_str(trait_span, token::get_ident(variant.node.name)), + cx.expr_uint(trait_span, fields.len()), + ) + ); + + let call = cx.expr_try(trait_span, call); + stmts.push(cx.stmt_expr(call)); + + let serialize_struct_sep = cx.ident_of("serialize_enum_sep"); + + for &FieldInfo { self_, span, .. } in fields.iter() { + let call = cx.expr_method_call( + span, + serializer, + serialize_struct_sep, + vec!( + cx.expr_addr_of(span, self_), + ) + ); + + let call = cx.expr_try(span, call); + + stmts.push(cx.stmt_expr(call)); + } + + let call = cx.expr_method_call( + trait_span, + serializer, + cx.ident_of("serialize_enum_end"), + vec!() + ); + + cx.expr_block(cx.block(trait_span, stmts, Some(call))) + } + + _ => cx.bug("expected Struct or EnumMatching in deriving_serializable") + } +}