Skip to content

Commit ec5249e

Browse files
ascjonestomusdrw
authored andcommitted
Use procedural macros (#340)
* Empty rpc trait with proc macro attribute * Generate skeleton to_delegate method * Move delegates to core - is this the right place * WIP: wrap rpc methods in to_delegate * Generate basic to_delegate * Add derive examples and tests * Add async rpc method example * Fix extern crate hygiene * Add where clause bounds to to_delegate function * WIP: Support one way serialisation for generics * Add Serialize bounds to generic RPC return types * Add Serialize bounds for RPC args * Clean up imports * Refactor and read aliases from rpc trait * Wire up aliases * Derive rpc with metadata associated type * Refactor * Rename trait level attribute to rpc * Refactor: move entry point function into lib * WIP: implement pubsub * Implement pubsub macro * Register pub/sub method aliases * Remove delegates file prematurely copied to core * Fix TCP tests? * Inline rpc method registration Removes need for Wrap* types * Extract return type when creating RpcMethod * Handle methods with no params * WIP: Add support for optional trailing params * Handle optional trailing params * Bring back aliases * Inline into_future to remove extraneous types/fns * Refactor extra args for use in pubsub * WIP: migrate pubsub delegate registration * Extract special args from delegate * Register sub/unsub using generated delgate closure * WIP: move macro delegates to core and pubsub * WIP: refactor to handle subscription method * Handle pubsub error * Add pub/sub IoDelegate * Refactor and fix pubsub compiler errors * Restore original delegates file and fix auto_args I've copied and split the delegates file into core and pubsub for use with proc macros, was originally reusing it in classic macros but putting it back * Wrap subscriber in typed subscriber * Handle pubsub unsubscribe method * Uncomment pubsub example * Unsubscribe optional metadata * Copy macro tests over and make them compile with derive * Fix pubsub trailing test * Rename to_delegate * Remove errant printlns * Copy and modify module docs from macros * Fix derive doc test * Mark subscribe/unsubscribe methods with attribute * Generic type param with metadata example, update some error messages * Refactor: extract a couple of functions for readability * Add compiletests to test compile errors * Remove test both for now * Remove top level future imports * Fix unsubscribe missing import * Correct derive crate author * Rust 2018: cargo fix --edition --package jsonrpc-derive * Remove redundant `extern crate` rust 2018 * Fix doc tests for edition 2018 * Add missing_docs warning * Replace &str to_string() with into() * Move all magic strings to constants * WIP * Bring pubsub methods back into same trait * Geoup pubsub methods by subscription name * Refactor attribute parsing * Update compilefail tests for new pubsub * Fix combined rpc and pubsub trait * Fix compilefail tests * added some tests (failing) * Remove multiple trailing params tests, will implement in later PR * Fix parse of single param tuple with trailing comma * Specify subscription name in missing annotation error * Add tests for trailing args * Handle single trailing param * Replace extern crates in macro output * Reduce recursion limit * Add comment for `to_delegate` trait method * Deprecate `to_delegate` method generated by jsonrpc-macros * Update README with derive example * Add pubsub example and rpc attribute docs * Remove commented out code * Bump major version
1 parent 789c74d commit ec5249e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2148
-48
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"http",
55
"ipc",
66
"macros",
7+
"derive",
78
"minihttp",
89
"pubsub",
910
"pubsub/more-examples",

README.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets`
1818
- [jsonrpc-tcp-server](./tcp) [![crates.io][tcp-server-image]][tcp-server-url]
1919
- [jsonrpc-ws-server](./ws)
2020
- [jsonrpc-stdio-server](./stdio)
21-
- [jsonrpc-macros](./macros) [![crates.io][macros-image]][macros-url]
21+
- [jsonrpc-macros](./macros) [![crates.io][macros-image]][macros-url] *deprecated:* use `derive` instead
22+
- [jsonrpc-derive](./derive)
2223
- [jsonrpc-server-utils](./server-utils) [![crates.io][server-utils-image]][server-utils-url]
2324
- [jsonrpc-pubsub](./pubsub) [![crates.io][pubsub-image]][pubsub-url]
2425

@@ -38,7 +39,8 @@ Transport-agnostic `core` and transport servers for `http`, `ipc`, `websockets`
3839
## Examples
3940

4041
- [core](./core/examples)
41-
- [macros](./macros/examples)
42+
- [derive](./derive/examples)
43+
- [macros](./macros/examples) *deprecated*
4244
- [pubsub](./pubsub/examples)
4345

4446
### Basic Usage (with HTTP transport)
@@ -65,21 +67,17 @@ fn main() {
6567
}
6668
```
6769

68-
### Basic usage with macros
70+
### Basic usage with derive
6971

7072
```rust
71-
extern crate jsonrpc_core;
72-
#[macro_use]
73-
extern crate jsonrpc_macros;
74-
7573
use jsonrpc_core::Result;
74+
use jsonrpc_derive::rpc;
7675

77-
build_rpc_trait! {
78-
pub trait Rpc {
79-
/// Adds two numbers and returns a result
80-
#[rpc(name = "add")]
81-
fn add(&self, u64, u64) -> Result<u64>;
82-
}
76+
#[rpc]
77+
pub trait Rpc {
78+
/// Adds two numbers and returns a result
79+
#[rpc(name = "add")]
80+
fn add(&self, u64, u64) -> Result<u64>;
8381
}
8482

8583
pub struct RpcImpl;
@@ -89,7 +87,6 @@ impl Rpc for RpcImpl {
8987
}
9088
}
9189

92-
9390
fn main() {
9491
let mut io = jsonrpc_core::IoHandler::new();
9592
io.extend_with(RpcImpl.to_delegate())

core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ homepage = "https://github.com/paritytech/jsonrpc"
44
repository = "https://github.com/paritytech/jsonrpc"
55
license = "MIT"
66
name = "jsonrpc-core"
7-
version = "9.0.0"
7+
version = "10.0.0"
88
authors = ["debris <[email protected]>"]
99
keywords = ["jsonrpc", "json-rpc", "json", "rpc", "serde"]
1010
documentation = "https://paritytech.github.io/jsonrpc/jsonrpc_core/index.html"

core/src/delegates.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! Delegate rpc calls
2+
3+
use std::sync::Arc;
4+
use std::collections::HashMap;
5+
6+
use types::{Params, Value, Error};
7+
use calls::{Metadata, RemoteProcedure, RpcMethod, RpcNotification};
8+
use futures::IntoFuture;
9+
use BoxFuture;
10+
11+
struct DelegateAsyncMethod<T, F> {
12+
delegate: Arc<T>,
13+
closure: F,
14+
}
15+
16+
impl<T, M, F, I> RpcMethod<M> for DelegateAsyncMethod<T, F> where
17+
M: Metadata,
18+
F: Fn(&T, Params) -> I,
19+
I: IntoFuture<Item = Value, Error = Error>,
20+
T: Send + Sync + 'static,
21+
F: Send + Sync + 'static,
22+
I::Future: Send + 'static,
23+
{
24+
fn call(&self, params: Params, _meta: M) -> BoxFuture<Value> {
25+
let closure = &self.closure;
26+
Box::new(closure(&self.delegate, params).into_future())
27+
}
28+
}
29+
30+
struct DelegateMethodWithMeta<T, F> {
31+
delegate: Arc<T>,
32+
closure: F,
33+
}
34+
35+
impl<T, M, F, I> RpcMethod<M> for DelegateMethodWithMeta<T, F> where
36+
M: Metadata,
37+
F: Fn(&T, Params, M) -> I,
38+
I: IntoFuture<Item = Value, Error = Error>,
39+
T: Send + Sync + 'static,
40+
F: Send + Sync + 'static,
41+
I::Future: Send + 'static,
42+
{
43+
fn call(&self, params: Params, meta: M) -> BoxFuture<Value> {
44+
let closure = &self.closure;
45+
Box::new(closure(&self.delegate, params, meta).into_future())
46+
}
47+
}
48+
49+
struct DelegateNotification<T, F> {
50+
delegate: Arc<T>,
51+
closure: F,
52+
}
53+
54+
impl<T, M, F> RpcNotification<M> for DelegateNotification<T, F> where
55+
F: Fn(&T, Params) + 'static,
56+
F: Send + Sync + 'static,
57+
T: Send + Sync + 'static,
58+
M: Metadata,
59+
{
60+
fn execute(&self, params: Params, _meta: M) {
61+
let closure = &self.closure;
62+
closure(&self.delegate, params)
63+
}
64+
}
65+
66+
/// A set of RPC methods and notifications tied to single `delegate` struct.
67+
pub struct IoDelegate<T, M = ()> where
68+
T: Send + Sync + 'static,
69+
M: Metadata,
70+
{
71+
delegate: Arc<T>,
72+
methods: HashMap<String, RemoteProcedure<M>>,
73+
}
74+
75+
impl<T, M> IoDelegate<T, M> where
76+
T: Send + Sync + 'static,
77+
M: Metadata,
78+
{
79+
/// Creates new `IoDelegate`
80+
pub fn new(delegate: Arc<T>) -> Self {
81+
IoDelegate {
82+
delegate: delegate,
83+
methods: HashMap::new(),
84+
}
85+
}
86+
87+
/// Adds an alias to existing method.
88+
/// NOTE: Aliases are not transitive, i.e. you cannot create alias to an alias.
89+
pub fn add_alias(&mut self, from: &str, to: &str) {
90+
self.methods.insert(from.into(), RemoteProcedure::Alias(to.into()));
91+
}
92+
93+
/// Adds async method to the delegate.
94+
pub fn add_method<F, I>(&mut self, name: &str, method: F) where
95+
F: Fn(&T, Params) -> I,
96+
I: IntoFuture<Item = Value, Error = Error>,
97+
F: Send + Sync + 'static,
98+
I::Future: Send + 'static,
99+
{
100+
self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new(
101+
DelegateAsyncMethod {
102+
delegate: self.delegate.clone(),
103+
closure: method,
104+
}
105+
)));
106+
}
107+
108+
/// Adds async method with metadata to the delegate.
109+
pub fn add_method_with_meta<F, I>(&mut self, name: &str, method: F) where
110+
F: Fn(&T, Params, M) -> I,
111+
I: IntoFuture<Item = Value, Error = Error>,
112+
F: Send + Sync + 'static,
113+
I::Future: Send + 'static,
114+
{
115+
self.methods.insert(name.into(), RemoteProcedure::Method(Arc::new(
116+
DelegateMethodWithMeta {
117+
delegate: self.delegate.clone(),
118+
closure: method,
119+
}
120+
)));
121+
}
122+
123+
/// Adds notification to the delegate.
124+
pub fn add_notification<F>(&mut self, name: &str, notification: F) where
125+
F: Fn(&T, Params),
126+
F: Send + Sync + 'static,
127+
{
128+
self.methods.insert(name.into(), RemoteProcedure::Notification(Arc::new(
129+
DelegateNotification {
130+
delegate: self.delegate.clone(),
131+
closure: notification,
132+
}
133+
)));
134+
}
135+
}
136+
137+
impl<T, M> Into<HashMap<String, RemoteProcedure<M>>> for IoDelegate<T, M> where
138+
T: Send + Sync + 'static,
139+
M: Metadata,
140+
{
141+
fn into(self) -> HashMap<String, RemoteProcedure<M>> {
142+
self.methods
143+
}
144+
}

core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod io;
3737

3838
pub mod middleware;
3939
pub mod types;
40+
pub mod delegates;
4041

4142
/// A `Future` trait object.
4243
pub type BoxFuture<T> = Box<futures::Future<Item = T, Error = Error> + Send>;
@@ -45,6 +46,7 @@ pub type BoxFuture<T> = Box<futures::Future<Item = T, Error = Error> + Send>;
4546
pub type Result<T> = ::std::result::Result<T, Error>;
4647

4748
pub use calls::{RemoteProcedure, Metadata, RpcMethodSimple, RpcMethod, RpcNotificationSimple, RpcNotification};
49+
pub use delegates::IoDelegate;
4850
pub use io::{Compatibility, IoHandler, MetaIoHandler, FutureOutput, FutureResult, FutureResponse, FutureRpcResult};
4951
pub use middleware::{Middleware, Noop as NoopMiddleware};
5052
pub use types::*;

core/src/types/error.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! jsonrpc errors
2+
use std::fmt;
23
use serde::de::{Deserialize, Deserializer};
34
use serde::ser::{Serialize, Serializer};
45
use super::Value;
@@ -124,6 +125,18 @@ impl Error {
124125
}
125126
}
126127

128+
/// Creates `InvalidParams` for given parameter, with details.
129+
pub fn invalid_params_with_details<M, T>(message: M, details: T) -> Error where
130+
M: Into<String>,
131+
T: fmt::Debug
132+
{
133+
Error {
134+
code: ErrorCode::InvalidParams,
135+
message: format!("Invalid parameters: {}", message.into()),
136+
data: Some(Value::String(format!("{:?}", details))),
137+
}
138+
}
139+
127140
/// Creates new `InternalError`
128141
pub fn internal_error() -> Self {
129142
Self::new(ErrorCode::InternalError)

core/src/types/params.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ impl Params {
3131
Error::invalid_params(format!("Invalid params: {}.", e))
3232
})
3333
}
34+
35+
/// Check for no params, returns Err if any params
36+
pub fn expect_no_params(self) -> Result<(), Error> {
37+
match self {
38+
Params::None => Ok(()),
39+
Params::Array(ref v) if v.is_empty() => Ok(()),
40+
p => Err(Error::invalid_params_with_details("No parameters were expected", p)),
41+
}
42+
}
3443
}
3544

3645
#[cfg(test)]
@@ -75,4 +84,10 @@ mod tests {
7584
assert_eq!(err2.message, "Invalid params: invalid length 2, expected a tuple of size 3.");
7685
assert_eq!(err2.data, None);
7786
}
87+
88+
#[test]
89+
fn single_param_parsed_as_tuple() {
90+
let params: (u64,) = Params::Array(vec![Value::from(1)]).parse().unwrap();
91+
assert_eq!(params, (1,));
92+
}
7893
}

derive/Cargo.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "jsonrpc-derive"
3+
version = "10.0.0"
4+
authors = ["Parity Technologies <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
syn = { version = "^0.15.22", features = ["full", "extra-traits", "visit", "fold"] }
12+
proc-macro2 = "0.4"
13+
quote = "0.6"
14+
15+
[dev-dependencies]
16+
jsonrpc-core = { version = "10.0", path = "../core" }
17+
jsonrpc-pubsub = { version = "10.0", path = "../pubsub" }
18+
jsonrpc-tcp-server = { version = "10.0", path = "../tcp" }
19+
serde = "1.0"
20+
serde_derive = "1.0"
21+
serde_json = "1.0"
22+
compiletest_rs = { version = "*", features = ["stable"] }
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use serde_derive::{Serialize, Deserialize};
2+
3+
use jsonrpc_core::{IoHandler, Error, Result};
4+
use jsonrpc_core::futures::future::{self, FutureResult};
5+
use jsonrpc_derive::rpc;
6+
7+
// One is both parameter and a result so requires both Serialize and DeserializeOwned
8+
// Two is only a parameter so only requires DeserializeOwned
9+
// Three is only a result so only requires Serialize
10+
#[rpc]
11+
pub trait Rpc<One, Two, Three>
12+
{
13+
/// Get One type.
14+
#[rpc(name = "getOne")]
15+
fn one(&self) -> Result<One>;
16+
17+
/// Adds two numbers and returns a result
18+
#[rpc(name = "setTwo")]
19+
fn set_two(&self, _: Two) -> Result<()>;
20+
21+
#[rpc(name = "getThree")]
22+
fn get_three(&self) -> Result<Three>;
23+
24+
/// Performs asynchronous operation
25+
#[rpc(name = "beFancy")]
26+
fn call(&self, _: One) -> FutureResult<(One, u64), Error>;
27+
}
28+
29+
struct RpcImpl;
30+
31+
#[derive(Serialize, Deserialize)]
32+
struct InAndOut { foo: u64 }
33+
#[derive(Deserialize)]
34+
struct In {}
35+
#[derive(Serialize)]
36+
struct Out {}
37+
38+
impl Rpc<InAndOut, In, Out> for RpcImpl {
39+
fn one(&self) -> Result<InAndOut> {
40+
Ok(InAndOut { foo: 1u64 })
41+
}
42+
43+
fn set_two(&self, _x: In) -> Result<()> {
44+
Ok(())
45+
}
46+
47+
fn get_three(&self) -> Result<Out> {
48+
Ok(Out {})
49+
}
50+
51+
fn call(&self, num: InAndOut) -> FutureResult<(InAndOut, u64), Error> {
52+
crate::future::finished((InAndOut {foo: num.foo + 999}, num.foo))
53+
}
54+
}
55+
56+
fn main() {
57+
let mut io = IoHandler::new();
58+
59+
io.extend_with(Rpc::to_delegate(RpcImpl));
60+
}
61+

0 commit comments

Comments
 (0)