>>();
+
+ // 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!();
+ }
+}