#[macro_use]
extern crate error_chain;
extern crate jsonrpc_core;
#[macro_use]
extern crate serde_json;
extern crate serde;
#[macro_use]
extern crate log;
error_chain! {
errors {
TransportError {
description("Unable to send the JSON-RPC 2.0 request")
}
SerializeError {
description("Unable to serialize the method parameters")
}
ResponseError(msg: &'static str) {
description("Unable to deserialize the response into the desired type")
display("Unable to deserialize the response: {}", msg)
}
JsonRpcError(error: jsonrpc_core::types::error::Error) {
description("Method call returned JSON-RPC-2.0 error")
display("JSON-RPC-2.0 Error: {} ({})", error.code.description(), error.message)
}
}
}
pub trait Transport<E: ::std::error::Error + Send + 'static> {
fn send(&mut self, json_data: &[u8]) -> ::std::result::Result<Vec<u8>, E>;
}
#[macro_export]
macro_rules! jsonrpc_client {
(
$(#[$struct_doc:meta])*
pub struct $struct_name:ident {$(
$(#[$doc:meta])*
pub fn $method:ident(&mut $selff:ident $(, $arg_name:ident: $arg_ty:ty)*)
-> Result<$return_ty:ty>;
)*}
) => (
$(#[$struct_doc])*
pub struct $struct_name<E, T>
where E: ::std::error::Error + Send + 'static, T: $crate::Transport<E>
{
transport: T,
id: u64,
_error: ::std::marker::PhantomData<E>,
}
impl<E: ::std::error::Error + Send + 'static, T: $crate::Transport<E>> $struct_name<E, T> {
pub fn new(transport: T) -> Self {
$struct_name {
transport,
id: 0,
_error: ::std::marker::PhantomData,
}
}
$(
$(#[$doc])*
pub fn $method(&mut $selff $(, $arg_name: $arg_ty)*) -> $crate::Result<$return_ty> {
$selff.id += 1;
let method = stringify!($method);
let params = expand_params!($($arg_name,)*);
$crate::call_method(&mut $selff.transport, $selff.id, method, params)
}
)*
}
)
}
#[macro_export]
macro_rules! expand_params {
() => ([] as [(); 0]);
($($arg_name:ident,)+) => (($($arg_name,)+))
}
pub fn call_method<E, T, P, R>(transport: &mut T, id: u64, method: &str, params: P) -> Result<R>
where
E: ::std::error::Error + Send + 'static,
T: Transport<E>,
P: serde::Serialize,
for<'de> R: serde::Deserialize<'de>,
{
let request_json = format_request(id, method, params);
let request_raw = serde_json::to_vec(&request_json)
.chain_err(|| ErrorKind::SerializeError)?;
debug!("Sending JSON-RPC 2.0 request: {}", request_json);
let response_raw = transport
.send(&request_raw)
.chain_err(|| ErrorKind::TransportError)?;
parse_response::<R>(&response_raw, id)
}
fn format_request<P>(id: u64, method: &str, params: P) -> serde_json::Value
where
P: serde::Serialize,
{
json!({
"jsonrpc": "2.0",
"id": id,
"method": method,
"params": params,
})
}
fn parse_response<T>(response: &[u8], expected_id: u64) -> Result<T>
where
for<'de> T: serde::Deserialize<'de>,
{
let response_map = get_response_as_map(response)?;
let result_json = check_response_and_get_result(response_map, expected_id)?;
debug!("Received json result: {}", result_json);
serde_json::from_value::<T>(result_json).chain_err(|| {
ErrorKind::ResponseError("Result cannot deserialize to target type")
})
}
fn get_response_as_map(response: &[u8]) -> Result<serde_json::Map<String, serde_json::Value>> {
let response_json = serde_json::from_slice(response)
.chain_err(|| ErrorKind::ResponseError("Response is not valid json"))?;
if let serde_json::Value::Object(map) = response_json {
Ok(map)
} else {
Err(
ErrorKind::ResponseError("Response is not a json object").into(),
)
}
}
fn check_response_and_get_result(
mut response_map: serde_json::Map<String, serde_json::Value>,
expected_id: u64,
) -> Result<serde_json::Value> {
ensure!(
response_map.remove("jsonrpc") == Some(serde_json::Value::String("2.0".to_owned())),
ErrorKind::ResponseError("Response is not JSON-RPC 2.0 compatible")
);
ensure!(
response_map.remove("id") == Some(expected_id.into()),
ErrorKind::ResponseError("Response id not equal to request id")
);
if let Some(error_json) = response_map.remove("error") {
let error = json_value_to_rpc_error(error_json)
.chain_err(|| ErrorKind::ResponseError("Malformed error object"))?;
bail!(ErrorKind::JsonRpcError(error));
}
response_map.remove("result").ok_or(
ErrorKind::ResponseError("Response has no \"result\" field").into(),
)
}
fn json_value_to_rpc_error(
mut error_json: serde_json::Value,
) -> Result<jsonrpc_core::types::error::Error> {
let map = error_json
.as_object_mut()
.ok_or(ErrorKind::ResponseError("Error is not a json object"))?;
let code = map.remove("code")
.ok_or(ErrorKind::ResponseError("Error has no code field").into())
.and_then(|code| {
serde_json::from_value(code)
.chain_err(|| ErrorKind::ResponseError("Malformed code field in error"))
})?;
let message = map.get("message")
.and_then(|v| v.as_str())
.ok_or(ErrorKind::ResponseError(
"Error has no message field of string type",
))?
.to_owned();
Ok(jsonrpc_core::types::error::Error {
code: code,
message: message,
data: map.remove("data"),
})
}
jsonrpc_client!(
pub struct ExampleRpcClient {
pub fn nullary(&mut self) -> Result<()>;
pub fn echo(&mut self, input: String) -> Result<String>;
pub fn concat(&mut self, arg0: String, arg1: u64) -> Result<String>;
}
);
#[cfg(test)]
mod tests {
use super::*;
use std::io;
struct EchoTransport;
impl Transport<io::Error> for EchoTransport {
fn send(&mut self, json_data: &[u8]) -> ::std::result::Result<Vec<u8>, io::Error> {
let json = json!({
"jsonrpc": "2.0",
"id": 1,
"result": serde_json::from_slice::<serde_json::Value>(json_data).unwrap(),
});
Ok(serde_json::to_vec(&json).unwrap())
}
}
struct ErrorTransport;
impl Transport<io::Error> for ErrorTransport {
fn send(&mut self, _json_data: &[u8]) -> ::std::result::Result<Vec<u8>, io::Error> {
let json = json!({
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "This was an invalid request",
"data": [1, 2, 3],
}
});
Ok(serde_json::to_vec(&json).unwrap())
}
}
jsonrpc_client!(pub struct TestRpcClient {
pub fn ping(&mut self, arg0: String) -> Result<serde_json::Value>;
});
#[test]
fn echo() {
let mut client = TestRpcClient::new(EchoTransport);
let result = client.ping("Hello".to_string()).unwrap();
if let serde_json::Value::Object(mut map) = result {
assert_eq!(Some(serde_json::Value::String("2.0".to_string())), map.remove("jsonrpc"));
assert_eq!(Some(serde_json::Value::Number(1.into())), map.remove("id"));
assert_eq!(Some(serde_json::Value::String("ping".to_string())), map.remove("method"));
assert_eq!(Some(serde_json::Value::Array(vec!["Hello".into()])), map.remove("params"));
} else {
panic!("Invalid response type: {:?}", result);
}
}
#[test]
fn error() {
let mut client = TestRpcClient::new(ErrorTransport);
let error = client.ping("".to_string()).unwrap_err();
if let &ErrorKind::JsonRpcError(ref json_error) = error.kind() {
use jsonrpc_core::types::error::ErrorCode;
assert_eq!(ErrorCode::InvalidRequest, json_error.code);
assert_eq!("This was an invalid request", json_error.message);
assert_eq!(Some(json!{[1, 2, 3]}), json_error.data);
} else {
panic!("Wrong error kind");
}
}
}