Unverified Commit 09abbaaa authored by Niklas Adolfsson's avatar Niklas Adolfsson Committed by GitHub
Browse files

[proc macros] force proc macro api to return `Result` (#435)

* rewrite me

* require proc macro API to return result

* send unknown message when error is not CallError

* show example that auto cast StdError doesn't work

* register_*_method Into<Error>

* clippy

* replace generic errors with anyhow::Error

* fix nits

* example that anyhow::Error in register_method works

* CallError: add missing From impl

* [types]: add helper methods for Error types

The rationale is to make it possible for users to either use anyhow::Error or use the helper methods.

* fmt

* Revert "register_*_method Into<Error>"

This reverts commit 33b4fa28730b72647ba150659d3c0ab1937e524a.

* add better comment

* fix nit
parent 872a8d7f
......@@ -26,17 +26,17 @@
use jsonrpsee::{
proc_macros::rpc,
types::async_trait,
types::{async_trait, error::Error},
ws_client::WsClientBuilder,
ws_server::{RpcModule, SubscriptionSink, WsServerBuilder},
};
use std::net::SocketAddr;
#[rpc(client, server, namespace = "state")]
#[rpc(server, client, namespace = "state")]
pub trait Rpc {
/// Async method call example.
#[method(name = "getPairs")]
async fn storage_pairs(&self, prefix: usize, hash: Option<u128>) -> Vec<usize>;
async fn storage_pairs(&self, prefix: usize, hash: Option<u128>) -> Result<Vec<usize>, Error>;
/// Subscription that take `Option<Vec<u8>>` as input and produces output `Vec<usize>`.
#[subscription(name = "subscribeStorage", unsub = "unsubscribeStorage", item = Vec<usize>)]
......@@ -47,8 +47,8 @@ pub struct RpcServerImpl;
#[async_trait]
impl RpcServer for RpcServerImpl {
async fn storage_pairs(&self, _prefix: usize, _hash: Option<u128>) -> Vec<usize> {
vec![1, 2, 3, 4]
async fn storage_pairs(&self, _prefix: usize, _hash: Option<u128>) -> Result<Vec<usize>, Error> {
Ok(vec![1, 2, 3, 4])
}
fn subscribe_storage(&self, mut sink: SubscriptionSink, keys: Option<Vec<u8>>) {
......
......@@ -64,7 +64,7 @@ impl HttpClientBuilder {
/// Build the HTTP client with target to connect to.
pub fn build(self, target: impl AsRef<str>) -> Result<HttpClient, Error> {
let transport =
HttpTransportClient::new(target, self.max_request_body_size).map_err(|e| Error::Transport(Box::new(e)))?;
HttpTransportClient::new(target, self.max_request_body_size).map_err(|e| Error::Transport(e.into()))?;
Ok(HttpClient { transport, request_id: AtomicU64::new(0), request_timeout: self.request_timeout })
}
}
......@@ -94,7 +94,7 @@ impl Client for HttpClient {
match tokio::time::timeout(self.request_timeout, fut).await {
Ok(Ok(ok)) => Ok(ok),
Err(_) => Err(Error::RequestTimeout),
Ok(Err(e)) => Err(Error::Transport(Box::new(e))),
Ok(Err(e)) => Err(Error::Transport(e.into())),
}
}
......@@ -111,7 +111,7 @@ impl Client for HttpClient {
let body = match tokio::time::timeout(self.request_timeout, fut).await {
Ok(Ok(body)) => body,
Err(_e) => return Err(Error::RequestTimeout),
Ok(Err(e)) => return Err(Error::Transport(Box::new(e))),
Ok(Err(e)) => return Err(Error::Transport(e.into())),
};
let response: JsonRpcResponse<_> = match serde_json::from_slice(&body) {
......@@ -152,7 +152,7 @@ impl Client for HttpClient {
let body = match tokio::time::timeout(self.request_timeout, fut).await {
Ok(Ok(body)) => body,
Err(_e) => return Err(Error::RequestTimeout),
Ok(Err(e)) => return Err(Error::Transport(Box::new(e))),
Ok(Err(e)) => return Err(Error::Transport(e.into())),
};
let rps: Vec<JsonRpcResponse<_>> = match serde_json::from_slice(&body) {
......
......@@ -198,16 +198,16 @@ mod new;
///
/// // RPC is moved into a separate module to clearly show names of generated entities.
/// mod rpc_impl {
/// use jsonrpsee::{proc_macros::rpc, types::async_trait, ws_server::SubscriptionSink};
/// use jsonrpsee::{proc_macros::rpc, types::{async_trait, JsonRpcResult}, ws_server::SubscriptionSink};
///
/// // Generate both server and client implementations, prepend all the methods with `foo_` prefix.
/// #[rpc(client, server, namespace = "foo")]
/// pub trait Rpc {
/// #[method(name = "foo")]
/// async fn async_method(&self, param_a: u8, param_b: String) -> u16;
/// async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult<u16>;
///
/// #[method(name = "bar")]
/// fn sync_method(&self) -> u16;
/// fn sync_method(&self) -> JsonRpcResult<u16>;
///
/// #[subscription(name = "sub", unsub = "unsub", item = String)]
/// fn sub(&self);
......@@ -220,12 +220,12 @@ mod new;
/// // Note that the trait name we use is `RpcServer`, not `Rpc`!
/// #[async_trait]
/// impl RpcServer for RpcServerImpl {
/// async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 {
/// 42u16
/// async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult<u16> {
/// Ok(42u16)
/// }
///
/// fn sync_method(&self) -> u16 {
/// 10u16
/// fn sync_method(&self) -> JsonRpcResult<u16> {
/// Ok(10u16)
/// }
///
/// // We could've spawned a `tokio` future that yields values while our program works,
......
......@@ -52,7 +52,7 @@ impl RpcDescription {
// `returns` represent the return type of the *rust method* (`Result< <..>, jsonrpsee::Error`).
let (called_method, returns) = if let Some(returns) = &method.returns {
let called_method = quote::format_ident!("request");
let returns = quote! { Result<#returns, #jrps_error> };
let returns = quote! { #returns };
(called_method, returns)
} else {
......
......@@ -25,8 +25,6 @@ impl RpcDescription {
}
};
// panic!("{}", trait_impl);
Ok(trait_impl)
}
......@@ -83,7 +81,7 @@ impl RpcDescription {
let rpc_method_name = self.rpc_identifier(&method.name);
// `parsing` is the code associated with parsing structure from the
// provided `RpcParams` object.
// `params_seq` is the comma-delimited sequence of parametsrs.
// `params_seq` is the comma-delimited sequence of parameters.
let (parsing, params_seq) = self.render_params_decoding(&method.params);
check_name(rpc_method_name.clone(), rust_method_name.span());
......@@ -93,7 +91,7 @@ impl RpcDescription {
rpc.register_async_method(#rpc_method_name, |params, context| {
let fut = async move {
#parsing
Ok(context.as_ref().#rust_method_name(#params_seq).await)
context.as_ref().#rust_method_name(#params_seq).await
};
Box::pin(fut)
})
......@@ -102,7 +100,7 @@ impl RpcDescription {
handle_register_result(quote! {
rpc.register_method(#rpc_method_name, |params, context| {
#parsing
Ok(context.#rust_method_name(#params_seq))
context.#rust_method_name(#params_seq)
})
})
}
......@@ -121,7 +119,7 @@ impl RpcDescription {
let rpc_unsub_name = self.rpc_identifier(&sub.unsub_method);
// `parsing` is the code associated with parsing structure from the
// provided `RpcParams` object.
// `params_seq` is the comma-delimited sequence of parametsrs.
// `params_seq` is the comma-delimited sequence of parameters.
let (parsing, params_seq) = self.render_params_decoding(&sub.params);
check_name(rpc_sub_name.clone(), rust_method_name.span());
......
//! Example of using proc macro to generate working client and server.
use jsonrpsee::types::Error;
use jsonrpsee_proc_macros::rpc;
use std::borrow::Cow;
#[rpc(client, server, namespace = "foo")]
pub trait Rpc {
#[method(name = "foo")]
async fn async_method(&self, param_a: u8, param_b: Option<Cow<'_, str>>) -> u16;
async fn async_method(&self, param_a: u8, param_b: Option<Cow<'_, str>>) -> Result<u16, Error>;
#[method(name = "bar")]
fn sync_method(&self) -> u16;
fn sync_method(&self) -> Result<u16, Error>;
#[subscription(name = "sub", unsub = "unsub", item = String)]
fn sub(&self);
......
......@@ -2,7 +2,7 @@
use jsonrpsee::{
proc_macros::rpc,
types::async_trait,
types::{async_trait, JsonRpcResult},
ws_client::*,
ws_server::{SubscriptionSink, WsServerBuilder},
};
......@@ -11,10 +11,10 @@ use std::{net::SocketAddr, sync::mpsc::channel};
#[rpc(client, server, namespace = "foo")]
pub trait Rpc {
#[method(name = "foo")]
async fn async_method(&self, param_a: u8, param_b: String) -> u16;
async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult<u16>;
#[method(name = "bar")]
fn sync_method(&self) -> u16;
fn sync_method(&self) -> JsonRpcResult<u16>;
#[subscription(name = "sub", unsub = "unsub", item = String)]
fn sub(&self);
......@@ -27,12 +27,12 @@ pub struct RpcServerImpl;
#[async_trait]
impl RpcServer for RpcServerImpl {
async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 {
42u16
async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult<u16> {
Ok(42u16)
}
fn sync_method(&self) -> u16 {
10u16
fn sync_method(&self) -> JsonRpcResult<u16> {
Ok(10u16)
}
fn sub(&self, mut sink: SubscriptionSink) {
......
//! Example of using proc macro to generate working client and server.
use jsonrpsee::proc_macros::rpc;
use jsonrpsee::{proc_macros::rpc, types::JsonRpcResult};
#[rpc(client)]
pub trait Rpc {
#[method(name = "foo")]
async fn async_method(&self, param_a: u8, param_b: String) -> u16;
async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult<u16>;
#[method(name = "bar")]
fn sync_method(&self) -> u16;
fn sync_method(&self) -> JsonRpcResult<u16>;
#[subscription(name = "sub", unsub = "unsub", item = String)]
fn sub(&self);
......
use jsonrpsee::{
proc_macros::rpc,
types::async_trait,
types::{async_trait, JsonRpcResult},
ws_server::{SubscriptionSink, WsServerBuilder},
};
use std::{net::SocketAddr, sync::mpsc::channel};
......@@ -8,10 +8,10 @@ use std::{net::SocketAddr, sync::mpsc::channel};
#[rpc(server)]
pub trait Rpc {
#[method(name = "foo")]
async fn async_method(&self, param_a: u8, param_b: String) -> u16;
async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult<u16>;
#[method(name = "bar")]
fn sync_method(&self) -> u16;
fn sync_method(&self) -> JsonRpcResult<u16>;
#[subscription(name = "sub", unsub = "unsub", item = String)]
fn sub(&self);
......@@ -21,12 +21,12 @@ pub struct RpcServerImpl;
#[async_trait]
impl RpcServer for RpcServerImpl {
async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 {
42u16
async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult<u16> {
Ok(42u16)
}
fn sync_method(&self) -> u16 {
10u16
fn sync_method(&self) -> JsonRpcResult<u16> {
Ok(10u16)
}
fn sub(&self, mut sink: SubscriptionSink) {
......
......@@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc;
#[rpc(client, server)]
pub trait NoMethodName {
#[method()]
async fn async_method(&self) -> u8;
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
}
fn main() {}
......@@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc;
#[rpc(client, server)]
pub trait UnexpectedField {
#[method(name = "foo", magic = false)]
async fn async_method(&self) -> u8;
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
}
fn main() {}
......@@ -6,7 +6,7 @@ pub trait AssociatedConst {
const WOO: usize;
#[method(name = "foo")]
async fn async_method(&self) -> u8;
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
}
#[rpc(client, server)]
......@@ -14,7 +14,7 @@ pub trait AssociatedType {
type Woo;
#[method(name = "foo")]
async fn async_method(&self) -> u8;
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
}
fn main() {}
use jsonrpsee::proc_macros::rpc;
use jsonrpsee::{proc_macros::rpc, types::JsonRpcResult};
// Associated items are forbidden.
#[rpc(client, server)]
pub trait MethodNameConflict {
#[method(name = "foo")]
async fn foo(&self) -> u8;
async fn foo(&self) -> JsonRpcResult<u8>;
#[method(name = "foo")]
async fn bar(&self) -> u8;
async fn bar(&self) -> JsonRpcResult<u8>;
}
fn main() {}
error: "foo" is already defined
--> $DIR/rpc_name_conflict.rs:10:11
|
10 | async fn bar(&self) -> u8;
10 | async fn bar(&self) -> JsonRpcResult<u8>;
| ^^^
......@@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc;
#[rpc()]
pub trait NoImpls {
#[method(name = "foo")]
async fn async_method(&self) -> u8;
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
}
fn main() {}
......@@ -3,7 +3,7 @@ use jsonrpsee::proc_macros::rpc;
// Method without type marker.
#[rpc(client, server)]
pub trait NotQualified {
async fn async_method(&self) -> u8;
async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
}
fn main() {}
error: Methods must have either 'method' or 'subscription' attribute
--> $DIR/rpc_not_qualified.rs:6:2
|
6 | async fn async_method(&self) -> u8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 | async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult<u8>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
......@@ -33,15 +33,19 @@ use jsonrpsee::{ws_client::*, ws_server::WsServerBuilder};
use serde_json::value::RawValue;
mod rpc_impl {
use jsonrpsee::{proc_macros::rpc, types::async_trait, ws_server::SubscriptionSink};
use jsonrpsee::{
proc_macros::rpc,
types::{async_trait, JsonRpcResult},
ws_server::SubscriptionSink,
};
#[rpc(client, server, namespace = "foo")]
pub trait Rpc {
#[method(name = "foo")]
async fn async_method(&self, param_a: u8, param_b: String) -> u16;
async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult<u16>;
#[method(name = "bar")]
fn sync_method(&self) -> u16;
fn sync_method(&self) -> JsonRpcResult<u16>;
#[subscription(name = "sub", unsub = "unsub", item = String)]
fn sub(&self);
......@@ -50,13 +54,13 @@ mod rpc_impl {
fn sub_with_params(&self, val: u32);
#[method(name = "params")]
fn params(&self, a: u8, b: &str) -> String {
format!("Called with: {}, {}", a, b)
fn params(&self, a: u8, b: &str) -> JsonRpcResult<String> {
Ok(format!("Called with: {}, {}", a, b))
}
#[method(name = "optional_params")]
fn optional_params(&self, a: u32, b: Option<u32>, c: Option<u32>) -> String {
format!("Called with: {}, {:?}, {:?}", a, b, c)
fn optional_params(&self, a: u32, b: Option<u32>, c: Option<u32>) -> JsonRpcResult<String> {
Ok(format!("Called with: {}, {:?}, {:?}", a, b, c))
}
#[method(name = "lifetimes")]
......@@ -66,13 +70,13 @@ mod rpc_impl {
b: &'_ str,
c: std::borrow::Cow<'_, str>,
d: Option<beef::Cow<'_, str>>,
) -> String {
format!("Called with: {}, {}, {}, {:?}", a, b, c, d)
) -> JsonRpcResult<String> {
Ok(format!("Called with: {}, {}, {}, {:?}", a, b, c, d))
}
#[method(name = "zero_copy_cow")]
fn zero_copy_cow(&self, a: std::borrow::Cow<'_, str>, b: beef::Cow<'_, str>) -> String {
format!("Zero copy params: {}, {}", matches!(a, std::borrow::Cow::Borrowed(_)), b.is_borrowed())
fn zero_copy_cow(&self, a: std::borrow::Cow<'_, str>, b: beef::Cow<'_, str>) -> JsonRpcResult<String> {
Ok(format!("Zero copy params: {}, {}", matches!(a, std::borrow::Cow::Borrowed(_)), b.is_borrowed()))
}
}
......@@ -80,12 +84,12 @@ mod rpc_impl {
#[async_trait]
impl RpcServer for RpcServerImpl {
async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 {
42u16
async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult<u16> {
Ok(42u16)
}
fn sync_method(&self) -> u16 {
10u16
fn sync_method(&self) -> JsonRpcResult<u16> {
Ok(10u16)
}
fn sub(&self, mut sink: SubscriptionSink) {
......
......@@ -11,6 +11,7 @@ documentation = "https://docs.rs/jsonrpsee-types"
[dependencies]
async-trait = "0.1"
anyhow = "1"
beef = { version = "0.5.1", features = ["impl_serde"] }
futures-channel = { version = "0.3.14", features = ["sink"] }
futures-util = { version = "0.3.14", default-features = false, features = ["std", "sink", "channel"] }
......
......@@ -136,7 +136,7 @@ where
Some(n) => match serde_json::from_value::<NotifResponse<Notif>>(n) {
Ok(NotifResponse::Ok(parsed)) => Ok(Some(parsed)),
Ok(NotifResponse::Err(e)) => Err(Error::SubscriptionClosed(e)),
Err(e) => Err(e.into()),
Err(e) => Err(Error::ParseError(e)),
},
None => Ok(None),
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment