use crate::{error, rpc, Error};
use futures::{
task::{Context, Poll},
Future,
};
use pin_project::pin_project;
use serde::de::DeserializeOwned;
use std::{marker::PhantomData, pin::Pin};
pub fn decode<T: serde::de::DeserializeOwned>(value: rpc::Value) -> error::Result<T> {
serde_json::from_value(value).map_err(Into::into)
}
#[pin_project]
#[derive(Debug)]
pub struct CallFuture<T, F> {
#[pin]
inner: F,
_marker: PhantomData<T>,
}
impl<T, F> CallFuture<T, F> {
pub fn new(inner: F) -> Self {
CallFuture {
inner,
_marker: PhantomData,
}
}
}
impl<T, F> Future for CallFuture<T, F>
where
T: serde::de::DeserializeOwned,
F: Future<Output = error::Result<rpc::Value>>,
{
type Output = error::Result<T>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
let x = ready!(this.inner.poll(ctx));
Poll::Ready(x.and_then(decode))
}
}
pub fn serialize<T: serde::Serialize>(t: &T) -> rpc::Value {
serde_json::to_value(t).expect("Types never fail to serialize.")
}
pub fn to_string<T: serde::Serialize>(request: &T) -> String {
serde_json::to_string(&request).expect("String serialization never fails.")
}
pub fn build_request(id: usize, method: &str, params: Vec<rpc::Value>) -> rpc::Call {
rpc::Call::MethodCall(rpc::MethodCall {
jsonrpc: Some(rpc::Version::V2),
method: method.into(),
params: rpc::Params::Array(params),
id: rpc::Id::Num(id as u64),
})
}
pub fn to_response_from_slice(response: &[u8]) -> error::Result<rpc::Response> {
arbitrary_precision_deserialize_workaround(response).map_err(|e| Error::InvalidResponse(format!("{:?}", e)))
}
pub fn arbitrary_precision_deserialize_workaround<T>(bytes: &[u8]) -> Result<T, serde_json::Error>
where
T: DeserializeOwned,
{
if cfg!(feature = "arbitrary_precision") {
serde_json::from_value(serde_json::from_slice(bytes)?)
} else {
serde_json::from_slice(bytes)
}
}
pub fn to_notification_from_slice(notification: &[u8]) -> error::Result<rpc::Notification> {
serde_json::from_slice(notification).map_err(|e| error::Error::InvalidResponse(format!("{:?}", e)))
}
pub fn to_results_from_outputs(outputs: Vec<rpc::Output>) -> error::Result<Vec<error::Result<rpc::Value>>> {
Ok(outputs.into_iter().map(to_result_from_output).collect())
}
pub fn to_result_from_output(output: rpc::Output) -> error::Result<rpc::Value> {
match output {
rpc::Output::Success(success) => Ok(success.result),
rpc::Output::Failure(failure) => Err(error::Error::Rpc(failure.error)),
}
}
#[macro_use]
#[cfg(test)]
pub mod tests {
macro_rules! rpc_test {
(
$namespace: ident : $name: ident : $test_name: ident $(, $param: expr)+ => $method: expr, $results: expr;
$returned: expr => $expected: expr
) => {
#[test]
fn $test_name() {
let mut transport = $crate::transports::test::TestTransport::default();
transport.set_response($returned);
let result = {
let eth = $namespace::new(&transport);
eth.$name($($param.into(), )+)
};
transport.assert_request($method, &$results.into_iter().map(Into::into).collect::<Vec<_>>());
transport.assert_no_more_requests();
let result = futures::executor::block_on(result);
assert_eq!(result, Ok($expected.into()));
}
};
(
$namespace: ident : $name: ident $(, $param: expr)+ => $method: expr, $results: expr;
$returned: expr => $expected: expr
) => {
rpc_test! (
$namespace : $name : $name $(, $param)+ => $method, $results;
$returned => $expected
);
};
(
$namespace: ident: $name: ident: $test_name: ident => $method: expr;
$returned: expr => $expected: expr
) => {
#[test]
fn $test_name() {
let mut transport = $crate::transports::test::TestTransport::default();
transport.set_response($returned);
let result = {
let eth = $namespace::new(&transport);
eth.$name()
};
transport.assert_request($method, &[]);
transport.assert_no_more_requests();
let result = futures::executor::block_on(result);
assert_eq!(result, Ok($expected.into()));
}
};
(
$namespace: ident: $name: ident => $method: expr;
$returned: expr => $expected: expr
) => {
rpc_test! (
$namespace: $name: $name => $method;
$returned => $expected
);
}
}
}