diff --git a/ipc/codegen/Cargo.toml b/ipc/codegen/Cargo.toml new file mode 100644 index 000000000..09d6a14bb --- /dev/null +++ b/ipc/codegen/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "ethcore-ipc-codegen" +version = "1.1.0" +authors = ["Nikolay Volf"] +license = "GPL-3.0" +description = "Macros to auto-generate implementations for ipc call" +build = "build.rs" +keywords = ["ipc", "codegen"] + +[features] +default = ["with-syntex"] +nightly = ["quasi_macros"] +nightly-testing = ["clippy"] +with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"] + +[build-dependencies] +quasi_codegen = { version = "^0.8.0", optional = true } +syntex = { version = "^0.30.0", optional = true } + +[dependencies] +aster = { version = "^0.14.0", default-features = false } +clippy = { version = "^0.*", optional = true } +quasi = { version = "^0.8.0", default-features = false } +quasi_macros = { version = "^0.8.0", optional = true } +syntex = { version = "^0.30.0", optional = true } +syntex_syntax = { version = "^0.30.0", optional = true } +"ethcore-ipc" = { path = "../rpc"} diff --git a/ipc/codegen/build.rs b/ipc/codegen/build.rs new file mode 100644 index 000000000..db9f5ac7e --- /dev/null +++ b/ipc/codegen/build.rs @@ -0,0 +1,45 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + + +#[cfg(feature = "with-syntex")] +mod inner { + extern crate syntex; + extern crate quasi_codegen; + + use std::env; + use std::path::Path; + + pub fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let mut registry = syntex::Registry::new(); + quasi_codegen::register(&mut registry); + + let src = Path::new("src/lib.rs.in"); + let dst = Path::new(&out_dir).join("lib.rs"); + + registry.expand("", &src, &dst).unwrap(); + } +} + +#[cfg(not(feature = "with-syntex"))] +mod inner { + pub fn main() {} +} + +fn main() { + inner::main(); +} diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs new file mode 100644 index 000000000..922fefb7b --- /dev/null +++ b/ipc/codegen/src/codegen.rs @@ -0,0 +1,284 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use aster; + +use syntax::ast::{ + MetaItem, + Item, + ImplItemKind, + ImplItem, + MethodSig, + Arg, + PatKind, + FunctionRetTy, +}; + +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{Annotatable, ExtCtxt}; +use syntax::ext::build::AstBuilder; +use syntax::ptr::P; + +pub struct Error; + +pub fn expand_ipc_implementation( + cx: &mut ExtCtxt, + span: Span, + meta_item: &MetaItem, + annotatable: &Annotatable, + push: &mut FnMut(Annotatable) +) { + let item = match *annotatable { + Annotatable::Item(ref item) => item, + _ => { + cx.span_err(meta_item.span, "`#[derive(Ipc)]` may only be applied to struct implementations"); + return; + } + }; + + let builder = aster::AstBuilder::new().span(span); + + let impl_item = match implement_interface(cx, &builder, &item, push) { + Ok(item) => item, + Err(Error) => { return; } + }; + + push(Annotatable::Item(impl_item)) +} + +fn field_name(builder: &aster::AstBuilder, arg: &Arg) -> ast::Ident { + match arg.pat.node { + PatKind::Ident(_, ref ident, _) => builder.id(ident.node), + _ => { panic!("unexpected param in interface: {:?}", arg.pat.node) } + } +} + +fn push_invoke_signature_aster ( + builder: &aster::AstBuilder, + implement: &ImplItem, + signature: &MethodSig, + push: &mut FnMut(Annotatable), +) -> Dispatch { + + let inputs = &signature.decl.inputs; + let (input_type_name, input_arg_names) = if inputs.len() > 0 { + let first_field_name = field_name(builder, &inputs[0]).name.as_str(); + if first_field_name == "self" && inputs.len() == 1 { (None, vec![]) } + else { + let skip = if first_field_name == "self" { 2 } else { 1 }; + let name_str = format!("{}_input", implement.ident.name.as_str()); + + let mut arg_names = Vec::new(); + let arg_name = format!("{}", field_name(builder, &inputs[skip-1]).name); + let mut tree = builder.item() + .attr().word("derive(Serialize, Deserialize)") + .attr().word("allow(non_camel_case_types)") + .struct_(name_str.as_str()) + .field(arg_name.as_str()).ty().build(inputs[skip-1].ty.clone()); + arg_names.push(arg_name); + for arg in inputs.iter().skip(skip) { + let arg_name = format!("{}", field_name(builder, &arg)); + tree = tree.field(arg_name.as_str()).ty().build(arg.ty.clone()); + arg_names.push(arg_name); + } + + push(Annotatable::Item(tree.build())); + (Some(name_str.to_owned()), arg_names) + } + } + else { + (None, vec![]) + }; + + let return_type_name = match signature.decl.output { + FunctionRetTy::Ty(ref ty) => { + let name_str = format!("{}_output", implement.ident.name.as_str()); + let tree = builder.item() + .attr().word("derive(Serialize, Deserialize)") + .attr().word("allow(non_camel_case_types)") + .struct_(name_str.as_str()) + .field(format!("payload")).ty().build(ty.clone()); + push(Annotatable::Item(tree.build())); + Some(name_str.to_owned()) + } + _ => None + }; + + Dispatch { + function_name: format!("{}", implement.ident.name.as_str()), + input_type_name: input_type_name, + input_arg_names: input_arg_names, + return_type_name: return_type_name, + } +} + +struct Dispatch { + function_name: String, + input_type_name: Option, + input_arg_names: Vec, + return_type_name: Option, +} + +fn implement_dispatch_arm_invoke( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + dispatch: &Dispatch, +) -> P +{ + let deserialize_expr = quote_expr!(cx, ::bincode::serde::deserialize_from(r, ::bincode::SizeLimit::Infinite).expect("ipc deserialization error, aborting")); + let input_type_id = builder.id(dispatch.input_type_name.clone().unwrap().as_str()); + let function_name = builder.id(dispatch.function_name.as_str()); + let output_type_id = builder.id(dispatch.return_type_name.clone().unwrap().as_str()); + + let input_args_exprs = dispatch.input_arg_names.iter().map(|ref arg_name| { + let arg_ident = builder.id(arg_name); + quote_expr!(cx, input. $arg_ident) + }).collect::>>(); + + // This is the expanded version of this: + // + // let invoke_serialize_stmt = quote_stmt!(cx, { + // ::bincode::serde::serialize(& $output_type_id { payload: self. $function_name ($hand_param_a, $hand_param_b) }, ::bincode::SizeLimit::Infinite).unwrap() + // }); + // + // But the above does not allow comma-separated expressions for arbitrary number + // of parameters ...$hand_param_a, $hand_param_b, ... $hand_param_n + let invoke_serialize_stmt = { + let ext_cx = &*cx; + ::quasi::parse_stmt_panic(&mut ::syntax::parse::new_parser_from_tts( + ext_cx.parse_sess(), + ext_cx.cfg(), + { + let _sp = ext_cx.call_site(); + let mut tt = ::std::vec::Vec::new(); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serde"), ::syntax::parse::token::ModName))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize"), ::syntax::parse::token::Plain))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And))); + tt.extend(::quasi::ToTokens::to_tokens(&output_type_id, ext_cx).into_iter()); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("payload"), ::syntax::parse::token::Plain))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Colon)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self"), ::syntax::parse::token::Plain))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); + tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter()); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); + + for arg_expr in input_args_exprs { + tt.extend(::quasi::ToTokens::to_tokens(&arg_expr, ext_cx).into_iter()); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma)); + } + + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Comma)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("bincode"), ::syntax::parse::token::ModName))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("SizeLimit"), ::syntax::parse::token::ModName))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Infinite"), ::syntax::parse::token::Plain))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap"), ::syntax::parse::token::Plain))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace))); + tt + })) + }; + quote_expr!(cx, { + let input: $input_type_id = $deserialize_expr; + $invoke_serialize_stmt + }) +} + +fn implement_dispatch_arm(cx: &ExtCtxt, builder: &aster::AstBuilder, index: u32, dispatch: &Dispatch) + -> ast::Arm +{ + let index_ident = builder.id(format!("{}", index).as_str()); + let invoke_expr = implement_dispatch_arm_invoke(cx, builder, dispatch); + quote_arm!(cx, $index_ident => { $invoke_expr } ) +} + +fn implement_interface( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + item: &Item, + push: &mut FnMut(Annotatable), +) -> Result, Error> { + let (generics, impl_items) = match item.node { + ast::ItemKind::Impl(_, _, ref generics, _, _, ref impl_items) => (generics, impl_items), + _ => { + cx.span_err( + item.span, + "`#[derive(Ipc)]` may only be applied to item implementations"); + return Err(Error); + } + }; + + let impl_generics = builder.from_generics(generics.clone()) + .add_ty_param_bound( + builder.path().global().ids(&["ethcore_ipc"]).build() + ) + .build(); + + let ty = builder.ty().path() + .segment(item.ident).with_generics(impl_generics.clone()).build() + .build(); + + let where_clause = &impl_generics.where_clause; + + let mut dispatch_table = Vec::new(); + for impl_item in impl_items { + if let ImplItemKind::Method(ref signature, _) = impl_item.node { + dispatch_table.push(push_invoke_signature_aster(builder, &impl_item, signature, push)); + } + } + let mut index = -1; + let dispatch_arms: Vec<_> = dispatch_table.iter() + .map(|dispatch| { index = index + 1; implement_dispatch_arm(cx, builder, index as u32, dispatch) }).collect(); + + Ok(quote_item!(cx, + impl $impl_generics ::ipc::IpcInterface<$ty> for $ty $where_clause { + fn dispatch(&self, r: &mut R) -> Vec + where R: ::std::io::Read + { + let mut method_num = vec![0u8;2]; + match r.read(&mut method_num) { + Ok(size) if size == 0 => { panic!("method id not supplied" ); } + Err(e) => { panic!("ipc read error: {:?}, aborting", e); } + _ => { } + } + match method_num[0] as u16 + (method_num[1] as u16)*256 { + $dispatch_arms + _ => vec![] + } + } + fn invoke(&self, _method_num: u16, _payload: &Option>, _w: &mut W) + where W: ::std::io::Write + { + } + + } + ).unwrap()) +} diff --git a/ipc/codegen/src/lib.rs b/ipc/codegen/src/lib.rs new file mode 100644 index 000000000..8129842ba --- /dev/null +++ b/ipc/codegen/src/lib.rs @@ -0,0 +1,67 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Codegen for IPC RPC + +#![cfg_attr(feature = "nightly-testing", plugin(clippy))] +#![cfg_attr(feature = "nightly-testing", feature(plugin))] +#![cfg_attr(feature = "nightly-testing", allow(used_underscore_binding))] +#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))] +#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))] + +extern crate aster; +extern crate quasi; + +#[cfg(feature = "with-syntex")] +extern crate syntex; + +#[cfg(feature = "with-syntex")] +#[macro_use] +extern crate syntex_syntax as syntax; + +#[cfg(not(feature = "with-syntex"))] +#[macro_use] +extern crate syntax; + +#[cfg(not(feature = "with-syntex"))] +extern crate rustc_plugin; + +extern crate ethcore_ipc as ipc; + +#[cfg(not(feature = "with-syntex"))] +use syntax::feature_gate::AttributeType; + +#[cfg(feature = "with-syntex")] +include!(concat!(env!("OUT_DIR"), "/lib.rs")); + +#[cfg(not(feature = "with-syntex"))] +include!("lib.rs.in"); + +#[cfg(feature = "with-syntex")] +pub fn register(reg: &mut syntex::Registry) { + reg.add_attr("feature(custom_derive)"); + reg.add_attr("feature(custom_attribute)"); + + reg.add_decorator("derive_Ipc", codegen::expand_ipc_implementation); +} + +#[cfg(not(feature = "with-syntex"))] +pub fn register(reg: &mut rustc_plugin::Registry) { + reg.register_syntax_extension( + syntax::parse::token::intern("derive_Ipc"), + syntax::ext::base::MultiDecorator( + Box::new(ser::expand_derive_serialize))); +} diff --git a/ipc/codegen/src/lib.rs.in b/ipc/codegen/src/lib.rs.in new file mode 100644 index 000000000..c942157b1 --- /dev/null +++ b/ipc/codegen/src/lib.rs.in @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +mod codegen; diff --git a/ipc/rpc/Cargo.toml b/ipc/rpc/Cargo.toml new file mode 100644 index 000000000..b8a05f543 --- /dev/null +++ b/ipc/rpc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ethcore-ipc" +version = "1.1.0" +authors = ["Nikolay Volf "] +license = "GPL-3.0" + +[features] + +[dependencies] diff --git a/ipc/rpc/src/interface.rs b/ipc/rpc/src/interface.rs new file mode 100644 index 000000000..111176b1f --- /dev/null +++ b/ipc/rpc/src/interface.rs @@ -0,0 +1,24 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! IPC RPC interface + +pub trait IpcInterface { + /// reads the message from io, dispatches the call and returns result + fn dispatch(&self, r: &mut R) -> Vec where R: ::std::io::Read; + /// encodes the invocation, writes payload and waits for result + fn invoke(&self, method_num: u16, params: &Option>, w: &mut W) where W: ::std::io::Write; +} diff --git a/ipc/rpc/src/lib.rs b/ipc/rpc/src/lib.rs new file mode 100644 index 000000000..5018a5cd2 --- /dev/null +++ b/ipc/rpc/src/lib.rs @@ -0,0 +1,20 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! IPC RPC interface + +pub mod interface; +pub use interface::IpcInterface; diff --git a/ipc/tests/Cargo.toml b/ipc/tests/Cargo.toml new file mode 100644 index 000000000..a4d07ae57 --- /dev/null +++ b/ipc/tests/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ethcore-ipc-tests" +version = "0.1.0" +authors = ["Nikolay Volf"] +build = "build.rs" + +[lib] +path = "run.rs" + +[dependencies] +"ethcore-ipc" = { path = "../rpc" } +bincode = "*" +serde = "0.7.0" + +[build-dependencies] +syntex = "0.30.0" +serde_codegen = "0.7.0" +"ethcore-ipc-codegen" = { path = "../codegen" } diff --git a/ipc/tests/build.rs b/ipc/tests/build.rs new file mode 100644 index 000000000..a960c4202 --- /dev/null +++ b/ipc/tests/build.rs @@ -0,0 +1,44 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +extern crate syntex; +extern crate ethcore_ipc_codegen as codegen; +extern crate serde_codegen; + +use std::env; +use std::path::Path; + +pub fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + + // ipc pass + { + let src = Path::new("service.rs.in"); + let dst = Path::new(&out_dir).join("service_ipc.rs"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } + + // serde pass + { + let src = Path::new(&out_dir).join("service_ipc.rs"); + let dst = Path::new(&out_dir).join("service_cg.rs"); + let mut registry = syntex::Registry::new(); + serde_codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } +} diff --git a/ipc/tests/examples.rs b/ipc/tests/examples.rs new file mode 100644 index 000000000..5a6fc69bf --- /dev/null +++ b/ipc/tests/examples.rs @@ -0,0 +1,36 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +#[cfg(test)] +mod tests { + + use super::super::socket::*; + use super::super::service::*; + use ipc::*; + + #[test] + fn call_service() { + // method_num = 0, f = 10 (method Service::commit) + let mut socket = TestSocket::new_ready(vec![0, 0, 0, 0, 0, 10]); + + let service = Service::new(); + assert_eq!(0, *service.commits.read().unwrap()); + + service.dispatch(&mut socket); + + assert_eq!(10, *service.commits.read().unwrap()); + } +} diff --git a/ipc/tests/run.rs b/ipc/tests/run.rs new file mode 100644 index 000000000..187b52d9d --- /dev/null +++ b/ipc/tests/run.rs @@ -0,0 +1,23 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +extern crate bincode; +extern crate ethcore_ipc as ipc; +extern crate serde; + +pub mod socket; +pub mod service; +mod examples; diff --git a/ipc/tests/service.rs b/ipc/tests/service.rs new file mode 100644 index 000000000..ecd977f61 --- /dev/null +++ b/ipc/tests/service.rs @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +include!(concat!(env!("OUT_DIR"), "/service_cg.rs")); diff --git a/ipc/tests/service.rs.in b/ipc/tests/service.rs.in new file mode 100644 index 000000000..64b43c2ef --- /dev/null +++ b/ipc/tests/service.rs.in @@ -0,0 +1,45 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::RwLock; + +pub struct Service { + pub commits: RwLock, + pub rollbacks: RwLock, +} + +#[derive(Ipc)] +impl Service { + fn commit(&self, f: u32) -> u32 { + let mut lock = self.commits.write().unwrap(); + *lock = *lock + f as usize; + f + } + pub fn rollback(&self, a: u32, b: u32) -> i32 { + let mut lock = self.rollbacks.write().unwrap(); + *lock = *lock + a as usize - b as usize; + (a - b) as i32 + } +} + +impl Service { + pub fn new() -> Service { + Service { + commits: RwLock::new(0usize), + rollbacks: RwLock::new(0usize), + } + } +} diff --git a/ipc/tests/socket.rs b/ipc/tests/socket.rs new file mode 100644 index 000000000..c740e872a --- /dev/null +++ b/ipc/tests/socket.rs @@ -0,0 +1,94 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::io::*; +use std::cmp; + +pub struct TestSocket { + pub read_buffer: Vec, + pub write_buffer: Vec, + pub cursor: usize, + pub buf_size: usize, +} + +impl Default for TestSocket { + fn default() -> Self { + TestSocket::new() + } +} + +impl TestSocket { + pub fn new() -> Self { + TestSocket { + read_buffer: vec![], + write_buffer: vec![], + cursor: 0, + buf_size: 0, + } + } + + pub fn new_buf(buf_size: usize) -> TestSocket { + TestSocket { + read_buffer: vec![], + write_buffer: vec![], + cursor: 0, + buf_size: buf_size, + } + } + + pub fn new_ready(data: Vec) -> TestSocket { + TestSocket { + read_buffer: data, + write_buffer: vec![], + cursor: 0, + buf_size: 0, + } + } +} + +impl Read for TestSocket { + fn read(&mut self, buf: &mut [u8]) -> Result { + let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len()); + let len = cmp::max(end_position - self.cursor, 0); + match len { + 0 => Ok(0), + _ => { + for i in self.cursor..end_position { + buf[i-self.cursor] = self.read_buffer[i]; + } + self.cursor = self.cursor + buf.len(); + Ok(len) + } + } + } +} + +impl Write for TestSocket { + fn write(&mut self, buf: &[u8]) -> Result { + if self.buf_size == 0 || buf.len() < self.buf_size { + self.write_buffer.extend(buf.iter().cloned()); + Ok(buf.len()) + } + else { + self.write_buffer.extend(buf.iter().take(self.buf_size).cloned()); + Ok(self.buf_size) + } + } + + fn flush(&mut self) -> Result<()> { + unimplemented!(); + } +}