Merge pull request #281 from ethcore/use_jsonrpc_macros
use jsonrpc-macros, fixes #277
This commit is contained in:
commit
83a1ba6375
|
@ -331,10 +331,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "4.0.0"
|
||||
source = "git+https://github.com/ethcore/jsonrpc.git#1500da1b9613a0a17fc0109d825f3ccc60199a53"
|
||||
source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da"
|
||||
dependencies = [
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_codegen 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -343,7 +343,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "6.1.1"
|
||||
source = "git+https://github.com/ethcore/jsonrpc.git#1500da1b9613a0a17fc0109d825f3ccc60199a53"
|
||||
source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da"
|
||||
dependencies = [
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
|
@ -351,6 +351,15 @@ dependencies = [
|
|||
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ethcore/jsonrpc.git#140257f1a726e9190bdaafeb4625b2a5400de4da"
|
||||
dependencies = [
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"serde 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
|
@ -632,17 +641,6 @@ dependencies = [
|
|||
"tokio-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.3.6"
|
||||
|
@ -804,6 +802,7 @@ dependencies = [
|
|||
"db 0.1.0",
|
||||
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miner 0.1.0",
|
||||
"network 0.1.0",
|
||||
|
@ -1250,6 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
|
||||
"checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
|
||||
"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
|
||||
"checksum jsonrpc-macros 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b"
|
||||
|
@ -1275,7 +1275,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
|
||||
"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c"
|
||||
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
|
||||
"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6"
|
||||
"checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621"
|
||||
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||
"checksum quasi 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94a532453b931a4483a5b2e40f0fe04aee35b6bc2c0eeec876f1bd2358a134d3"
|
||||
|
|
|
@ -14,7 +14,9 @@ rustc-serialize = "0.3"
|
|||
tokio-core = "0.1.1"
|
||||
serde_macros = { version = "0.8.0", optional = true }
|
||||
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||
jsonrpc-macros = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||
|
||||
sync = { path = "../sync" }
|
||||
serialization = { path = "../serialization" }
|
||||
chain = { path = "../chain" }
|
||||
|
|
|
@ -6,6 +6,8 @@ extern crate rustc_serialize;
|
|||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate jsonrpc_core;
|
||||
#[macro_use]
|
||||
extern crate jsonrpc_macros;
|
||||
extern crate jsonrpc_http_server;
|
||||
extern crate tokio_core;
|
||||
extern crate sync;
|
||||
|
|
|
@ -1,294 +0,0 @@
|
|||
// because we reuse the type names as idents in the macros as a dirty hack to
|
||||
// work around `concat_idents!` being unstable.
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
///! Automatically serialize and deserialize parameters around a strongly-typed function.
|
||||
|
||||
use super::errors;
|
||||
|
||||
use jsonrpc_core::{Error, Params, Value, from_params, to_value};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// Auto-generates an RPC trait from trait definition.
|
||||
///
|
||||
/// This just copies out all the methods, docs, and adds another
|
||||
/// function `to_delegate` which will automatically wrap each strongly-typed
|
||||
/// function in a wrapper which handles parameter and output type serialization.
|
||||
///
|
||||
/// RPC functions may come in a couple forms: async and synchronous.
|
||||
/// These are parsed with the custom `#[rpc]` attribute, which must follow
|
||||
/// documentation.
|
||||
///
|
||||
/// ## The #[rpc] attribute
|
||||
///
|
||||
/// Valid forms:
|
||||
/// - `#[rpc(name = "name_here")]` (a synchronous rpc function which should be bound to the given name)
|
||||
/// - `#[rpc(async, name = "name_here")]` (an async rpc function which should be bound to the given name)
|
||||
///
|
||||
/// Synchronous function format:
|
||||
/// `fn foo(&self, Param1, Param2, Param3) -> Out`.
|
||||
///
|
||||
/// Asynchronous RPC functions must come in this form:
|
||||
/// `fn foo(&self, Param1, Param2, Param3, Ready<Out>);
|
||||
///
|
||||
/// Anything else will be rejected by the code generator.
|
||||
macro_rules! build_rpc_trait {
|
||||
// entry-point. todo: make another for traits w/ bounds.
|
||||
(
|
||||
$(#[$t_attr: meta])*
|
||||
pub trait $name: ident {
|
||||
$(
|
||||
$( #[doc=$m_doc:expr] )*
|
||||
#[ rpc( $($t:tt)* ) ]
|
||||
fn $m_name: ident ( $($p: tt)* ) $( -> Result<$out: ty, Error> )* ;
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(#[$t_attr])*
|
||||
pub trait $name: Sized + Send + Sync + 'static {
|
||||
$(
|
||||
$(#[doc=$m_doc])*
|
||||
fn $m_name ( $($p)* ) $( -> Result<$out, Error> )* ;
|
||||
)*
|
||||
|
||||
/// Transform this into an `IoDelegate`, automatically wrapping
|
||||
/// the parameters.
|
||||
fn to_delegate(self) -> ::jsonrpc_core::IoDelegate<Self> {
|
||||
let mut del = ::jsonrpc_core::IoDelegate::new(self.into());
|
||||
$(
|
||||
build_rpc_trait!(WRAP del =>
|
||||
( $($t)* )
|
||||
fn $m_name ( $($p)* ) $( -> Result<$out, Error> )*
|
||||
);
|
||||
)*
|
||||
del
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
( WRAP $del: expr =>
|
||||
(name = $name: expr)
|
||||
fn $method: ident (&self $(, $param: ty)*) -> Result<$out: ty, Error>
|
||||
) => {
|
||||
$del.add_method($name, move |base, params| {
|
||||
(Self::$method as fn(&_ $(, $param)*) -> Result<$out, Error>).wrap_rpc(base, params)
|
||||
})
|
||||
};
|
||||
|
||||
( WRAP $del: expr =>
|
||||
(async, name = $name: expr)
|
||||
fn $method: ident (&self, Ready<$out: ty> $(, $param: ty)*)
|
||||
) => {
|
||||
$del.add_async_method($name, move |base, params, ready| {
|
||||
(Self::$method as fn(&_, Ready<$out> $(, $param)*)).wrap_rpc(base, params, ready)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// A wrapper type without an implementation of `Deserialize`
|
||||
/// which allows a special implementation of `Wrap` for functions
|
||||
/// that take a trailing default parameter.
|
||||
pub struct Trailing<T: Default + Deserialize>(pub T);
|
||||
|
||||
/// A wrapper type for `jsonrpc_core`'s weakly-typed `Ready` struct.
|
||||
pub struct Ready<T: Serialize> {
|
||||
inner: ::jsonrpc_core::Ready,
|
||||
_marker: ::std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Serialize> From<::jsonrpc_core::Ready> for Ready<T> {
|
||||
fn from(ready: ::jsonrpc_core::Ready) -> Self {
|
||||
Ready { inner: ready, _marker: ::std::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize> Ready<T> {
|
||||
/// Respond withthe asynchronous result.
|
||||
pub fn ready(self, result: Result<T, Error>) {
|
||||
self.inner.ready(result.map(to_value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper trait for synchronous RPC functions.
|
||||
pub trait Wrap<B: Send + Sync + 'static> {
|
||||
fn wrap_rpc(&self, base: &B, params: Params) -> Result<Value, Error>;
|
||||
}
|
||||
|
||||
/// Wrapper trait for asynchronous RPC functions.
|
||||
pub trait WrapAsync<B: Send + Sync + 'static> {
|
||||
fn wrap_rpc(&self, base: &B, params: Params, ready: ::jsonrpc_core::Ready);
|
||||
}
|
||||
|
||||
// special impl for no parameters.
|
||||
impl<B, OUT> Wrap<B> for fn(&B) -> Result<OUT, Error>
|
||||
where B: Send + Sync + 'static, OUT: Serialize
|
||||
{
|
||||
fn wrap_rpc(&self, base: &B, params: Params) -> Result<Value, Error> {
|
||||
::v1::helpers::params::expect_no_params(params)
|
||||
.and_then(|()| (self)(base))
|
||||
.map(to_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, OUT> WrapAsync<B> for fn(&B, Ready<OUT>)
|
||||
where B: Send + Sync + 'static, OUT: Serialize
|
||||
{
|
||||
fn wrap_rpc(&self, base: &B, params: Params, ready: ::jsonrpc_core::Ready) {
|
||||
match ::v1::helpers::params::expect_no_params(params) {
|
||||
Ok(()) => (self)(base, ready.into()),
|
||||
Err(e) => ready.ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// creates a wrapper implementation which deserializes the parameters,
|
||||
// calls the function with concrete type, and serializes the output.
|
||||
macro_rules! wrap {
|
||||
($($x: ident),+) => {
|
||||
|
||||
// synchronous implementation
|
||||
impl <
|
||||
BASE: Send + Sync + 'static,
|
||||
OUT: Serialize,
|
||||
$($x: Deserialize,)+
|
||||
> Wrap<BASE> for fn(&BASE, $($x,)+) -> Result<OUT, Error> {
|
||||
fn wrap_rpc(&self, base: &BASE, params: Params) -> Result<Value, Error> {
|
||||
from_params::<($($x,)+)>(params).and_then(|($($x,)+)| {
|
||||
(self)(base, $($x,)+)
|
||||
}).map(to_value)
|
||||
}
|
||||
}
|
||||
|
||||
// asynchronous implementation
|
||||
impl <
|
||||
BASE: Send + Sync + 'static,
|
||||
OUT: Serialize,
|
||||
$($x: Deserialize,)+
|
||||
> WrapAsync<BASE> for fn(&BASE, Ready<OUT>, $($x,)+ ) {
|
||||
fn wrap_rpc(&self, base: &BASE, params: Params, ready: ::jsonrpc_core::Ready) {
|
||||
match from_params::<($($x,)+)>(params) {
|
||||
Ok(($($x,)+)) => (self)(base, ready.into(), $($x,)+),
|
||||
Err(e) => ready.ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special impl for no parameters other than block parameter.
|
||||
impl<B, OUT, T> Wrap<B> for fn(&B, Trailing<T>) -> Result<OUT, Error>
|
||||
where B: Send + Sync + 'static, OUT: Serialize, T: Default + Deserialize
|
||||
{
|
||||
fn wrap_rpc(&self, base: &B, params: Params) -> Result<Value, Error> {
|
||||
let len = match params {
|
||||
Params::Array(ref v) => v.len(),
|
||||
Params::None => 0,
|
||||
_ => return Err(errors::invalid_params("not an array", "")),
|
||||
};
|
||||
|
||||
let (id,) = match len {
|
||||
0 => (T::default(),),
|
||||
1 => try!(from_params::<(T,)>(params)),
|
||||
_ => return Err(Error::invalid_params()),
|
||||
};
|
||||
|
||||
(self)(base, Trailing(id)).map(to_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, OUT, T> WrapAsync<B> for fn(&B, Ready<OUT>, Trailing<T>)
|
||||
where B: Send + Sync + 'static, OUT: Serialize, T: Default + Deserialize
|
||||
{
|
||||
fn wrap_rpc(&self, base: &B, params: Params, ready: ::jsonrpc_core::Ready) {
|
||||
let len = match params {
|
||||
Params::Array(ref v) => v.len(),
|
||||
Params::None => 0,
|
||||
_ => return ready.ready(Err(errors::invalid_params("not an array", ""))),
|
||||
};
|
||||
|
||||
let id = match len {
|
||||
0 => Ok((T::default(),)),
|
||||
1 => from_params::<(T,)>(params),
|
||||
_ => Err(Error::invalid_params()),
|
||||
};
|
||||
|
||||
match id {
|
||||
Ok((id,)) => (self)(base, ready.into(), Trailing(id)),
|
||||
Err(e) => ready.ready(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// similar to `wrap!`, but handles a single default trailing parameter
|
||||
// accepts an additional argument indicating the number of non-trailing parameters.
|
||||
macro_rules! wrap_with_trailing {
|
||||
($num: expr, $($x: ident),+) => {
|
||||
// synchronous implementation
|
||||
impl <
|
||||
BASE: Send + Sync + 'static,
|
||||
OUT: Serialize,
|
||||
$($x: Deserialize,)+
|
||||
TRAILING: Default + Deserialize,
|
||||
> Wrap<BASE> for fn(&BASE, $($x,)+ Trailing<TRAILING>) -> Result<OUT, Error> {
|
||||
fn wrap_rpc(&self, base: &BASE, params: Params) -> Result<Value, Error> {
|
||||
let len = match params {
|
||||
Params::Array(ref v) => v.len(),
|
||||
Params::None => 0,
|
||||
_ => return Err(errors::invalid_params("not an array", "")),
|
||||
};
|
||||
|
||||
let params = match len - $num {
|
||||
0 => from_params::<($($x,)+)>(params)
|
||||
.map(|($($x,)+)| ($($x,)+ TRAILING::default())),
|
||||
1 => from_params::<($($x,)+ TRAILING)>(params)
|
||||
.map(|($($x,)+ id)| ($($x,)+ id)),
|
||||
_ => Err(Error::invalid_params()),
|
||||
};
|
||||
|
||||
let ($($x,)+ id) = try!(params);
|
||||
(self)(base, $($x,)+ Trailing(id)).map(to_value)
|
||||
}
|
||||
}
|
||||
|
||||
// asynchronous implementation
|
||||
impl <
|
||||
BASE: Send + Sync + 'static,
|
||||
OUT: Serialize,
|
||||
$($x: Deserialize,)+
|
||||
TRAILING: Default + Deserialize,
|
||||
> WrapAsync<BASE> for fn(&BASE, Ready<OUT>, $($x,)+ Trailing<TRAILING>) {
|
||||
fn wrap_rpc(&self, base: &BASE, params: Params, ready: ::jsonrpc_core::Ready) {
|
||||
let len = match params {
|
||||
Params::Array(ref v) => v.len(),
|
||||
Params::None => 0,
|
||||
_ => return ready.ready(Err(errors::invalid_params("not an array", ""))),
|
||||
};
|
||||
|
||||
let params = match len - $num {
|
||||
0 => from_params::<($($x,)+)>(params)
|
||||
.map(|($($x,)+)| ($($x,)+ TRAILING::default())),
|
||||
1 => from_params::<($($x,)+ TRAILING)>(params)
|
||||
.map(|($($x,)+ id)| ($($x,)+ id)),
|
||||
_ => Err(Error::invalid_params()),
|
||||
};
|
||||
|
||||
match params {
|
||||
Ok(($($x,)+ id)) => (self)(base, ready.into(), $($x,)+ Trailing(id)),
|
||||
Err(e) => ready.ready(Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wrap!(A, B, C, D, E);
|
||||
wrap!(A, B, C, D);
|
||||
wrap!(A, B, C);
|
||||
wrap!(A, B);
|
||||
wrap!(A);
|
||||
|
||||
wrap_with_trailing!(5, A, B, C, D, E);
|
||||
wrap_with_trailing!(4, A, B, C, D);
|
||||
wrap_with_trailing!(3, A, B, C);
|
||||
wrap_with_trailing!(2, A, B);
|
||||
wrap_with_trailing!(1, A);
|
|
@ -5,22 +5,9 @@ mod codes {
|
|||
pub const EXECUTION_ERROR: i64 = -32015;
|
||||
}
|
||||
|
||||
|
||||
macro_rules! rpc_unimplemented {
|
||||
() => (Err(::v1::helpers::errors::unimplemented(None)))
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
use jsonrpc_core::{Error, ErrorCode, Value};
|
||||
|
||||
pub fn unimplemented(details: Option<String>) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::InternalError,
|
||||
message: "This request is not implemented yet. Please create an issue on Github repo.".into(),
|
||||
data: details.map(Value::String),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid_params<T: fmt::Debug>(param: &str, details: T) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::InvalidParams,
|
||||
|
@ -36,3 +23,4 @@ pub fn execution<T: fmt::Debug>(data: T) -> Error {
|
|||
data: Some(Value::String(format!("{:?}", data))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
#[macro_use]
|
||||
pub mod auto_args;
|
||||
#[macro_use]
|
||||
pub mod errors;
|
||||
mod params;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
///! Parameters parsing helpers
|
||||
|
||||
use jsonrpc_core::{Error, Params};
|
||||
use v1::helpers::errors;
|
||||
|
||||
pub fn expect_no_params(params: Params) -> Result<(), Error> {
|
||||
match params {
|
||||
Params::None => Ok(()),
|
||||
p => Err(errors::invalid_params("No parameters were expected", p)),
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use jsonrpc_core::Error;
|
||||
|
||||
use v1::helpers::auto_args::Wrap;
|
||||
use v1::types::{BlockTemplate, BlockTemplateRequest};
|
||||
|
||||
build_rpc_trait! {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use jsonrpc_core::Error;
|
||||
|
||||
use v1::helpers::auto_args::Wrap;
|
||||
use v1::types::RawTransaction;
|
||||
use v1::types::H256;
|
||||
|
||||
|
|
Loading…
Reference in New Issue