Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[proc macros]: only generate unsub method if not provided #702

Merged
merged 9 commits into from
Feb 18, 2022
2 changes: 1 addition & 1 deletion proc-macros/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn find_jsonrpsee_crate(http_name: &str, ws_name: &str) -> Result<proc_macro2::T
/// #[method(name = "call")]
/// fn call(&self, a: A) -> RpcResult<B>;
///
/// #[subscription(name = "sub", item = Vec<C>)]
/// #[subscription(name = "subscribe", item = Vec<C>)]
/// fn sub(&self) -> RpcResult<()>;
/// }
/// ```
Expand Down
14 changes: 10 additions & 4 deletions proc-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub(crate) mod visitor;
/// #[method(name = "bar")]
/// fn sync_method(&self) -> String;
///
/// #[subscription(name = "sub", unsub = "unsub", item = "String")]
/// #[subscription(name = "subscribe", item = "String")]
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
/// fn sub(&self);
/// }
/// ```
Expand Down Expand Up @@ -163,6 +163,8 @@ pub(crate) mod visitor;
///
/// - `name` (mandatory): name of the RPC method. Does not have to be the same as the Rust method name.
/// - `aliases`: list of name aliases for the RPC method as a comma separated string.
/// The aliases are kept outside namespace, so you need add that if you want aliases in the current
/// namespace.
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
/// - `blocking`: when set method execution will always spawn on a dedicated thread. Only usable with non-`async` methods.
/// - `param_kind`: kind of structure to use for parameter passing. Can be "array" or "map", defaults to "array".
///
Expand All @@ -179,7 +181,11 @@ pub(crate) mod visitor;
/// **Arguments:**
///
/// - `name` (mandatory): name of the RPC method. Does not have to be the same as the Rust method name.
/// - `unsub` (mandatory): name of the RPC method to unsubscribe from the subscription. Must not be the same as `name`.
/// - `unsubscribe` (optional): name of the RPC method to unsubscribe from the subscription. Must not be the same as `name`.
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
/// This is generated for you if the subscription name starts with `subscribe`-
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
/// - `aliases` (optional): aliases for `name`. The aliases are kept outside namespace,
/// so you need add that if you want aliases in the current namespace.
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
/// - `unsubscribe_aliases` (optional): Similar to `aliases` but for `unsubscribe`.
/// - `item` (mandatory): type of items yielded by the subscription. Note that it must be the type, not string.
/// - `param_kind`: kind of structure to use for parameter passing. Can be "array" or "map", defaults to "array".
///
Expand All @@ -188,7 +194,7 @@ pub(crate) mod visitor;
/// Rust method marked with the `subscription` attribute **must**:
///
/// - be synchronous;
/// - not have return value.
/// - return `RpcResult<()>`
///
/// Rust method marked with `subscription` attribute **may**:
///
Expand Down Expand Up @@ -220,7 +226,7 @@ pub(crate) mod visitor;
/// #[method(name = "baz", blocking)]
/// fn blocking_method(&self) -> RpcResult<u16>;
///
/// #[subscription(name = "sub", item = String)]
/// #[subscription(name = "sub", unsubscribe = "unsub", item = String)]
/// fn sub(&self) -> RpcResult<()>;
/// }
///
Expand Down
21 changes: 16 additions & 5 deletions proc-macros/src/rpc_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ pub struct RpcSubscription {

impl RpcSubscription {
pub fn from_item(attr: syn::Attribute, mut sub: syn::TraitItemMethod) -> syn::Result<Self> {
let [aliases, item, name, param_kind, unsubscribe_aliases] =
AttributeMeta::parse(attr)?.retain(["aliases", "item", "name", "param_kind", "unsubscribe_aliases"])?;
let [aliases, item, name, param_kind, unsubscribe, unsubscribe_aliases] = AttributeMeta::parse(attr)?
.retain(["aliases", "item", "name", "param_kind", "unsubscribe", "unsubscribe_aliases"])?;

let aliases = parse_aliases(aliases)?;
let map = name?.value::<NameMapping>()?;
Expand All @@ -150,7 +150,12 @@ impl RpcSubscription {

let sig = sub.sig.clone();
let docs = extract_doc_comments(&sub.attrs);
let unsubscribe = build_unsubscribe_method(&name);
let unsubscribe = match parse_subscribe(unsubscribe)? {
Some(unsub) => unsub,
None => build_unsubscribe_method(&name).expect(
&format!("Could not generate the unsubscribe method with name '{}'. You need to provide the name manually using the `unsubscribe` attribute in your RPC API definition", name),
),
};

let params: Vec<_> = sig
.inputs
Expand Down Expand Up @@ -335,10 +340,16 @@ fn parse_aliases(arg: Result<Argument, MissingArgument>) -> syn::Result<Vec<Stri
Ok(aliases.map(|a| a.list.into_iter().map(|lit| lit.value()).collect()).unwrap_or_default())
}

fn parse_subscribe(arg: Result<Argument, MissingArgument>) -> syn::Result<Option<String>> {
let unsub = optional(arg, Argument::string)?;

Ok(unsub)
}

fn find_attr<'a>(attrs: &'a [Attribute], ident: &str) -> Option<&'a Attribute> {
attrs.iter().find(|a| a.path.is_ident(ident))
}

fn build_unsubscribe_method(method: &str) -> String {
format!("unsubscribe{}", method.strip_prefix("subscribe").unwrap_or(method))
fn build_unsubscribe_method(method: &str) -> Option<String> {
method.strip_prefix("subscribe").map(|s| format!("unsubscribe{}", s))
}
7 changes: 4 additions & 3 deletions proc-macros/tests/ui/correct/alias_doesnt_use_namespace.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use jsonrpsee::{proc_macros::rpc, core::RpcResult};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

#[rpc(client, server, namespace = "myapi")]
pub trait Rpc {
/// Alias doesn't use the namespace so not duplicated.
/// Aliases doesn't use the namespace.
niklasad1 marked this conversation as resolved.
Show resolved Hide resolved
/// Thus, this will generate `myapi_getTemp` and `getTemp`.
#[method(name = "getTemp", aliases = ["getTemp"])]
async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult<u16>;

#[subscription(name = "getFood", item = String, aliases = ["getFood"], unsubscribe_aliases = ["unsubscribegetFood"])]
#[subscription(name = "subscribeGetFood", item = String, aliases = ["getFood"], unsubscribe_aliases = ["unsubscribegetFood"])]
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
fn sub(&self) -> RpcResult<()>;
}

Expand Down
6 changes: 3 additions & 3 deletions proc-macros/tests/ui/correct/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

use std::net::SocketAddr;

use jsonrpsee::core::{async_trait, client::ClientT, RpcResult};
use jsonrpsee::proc_macros::rpc;
use jsonrpsee::rpc_params;
use jsonrpsee::core::{async_trait, client::ClientT, RpcResult};
use jsonrpsee::ws_client::*;
use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder};

Expand All @@ -25,10 +25,10 @@ pub trait Rpc {
#[method(name = "bar")]
fn sync_method(&self) -> RpcResult<u16>;

#[subscription(name = "sub", item = String)]
#[subscription(name = "subscribe", item = String)]
fn sub(&self) -> RpcResult<()>;

#[subscription(name = "echo", aliases = ["ECHO"], item = u32, unsubscribe_aliases = ["NotInterested", "listenNoMore"])]
#[subscription(name = "echo", unsubscribe = "unsubscribeEcho", aliases = ["ECHO"], item = u32, unsubscribe_aliases = ["NotInterested", "listenNoMore"])]
fn sub_with_params(&self, val: u32) -> RpcResult<()>;

// This will send data to subscribers with the `method` field in the JSON payload set to `foo_subscribe_override`
Expand Down
4 changes: 2 additions & 2 deletions proc-macros/tests/ui/correct/only_client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Example of using proc macro to generate working client and server.

use jsonrpsee::{proc_macros::rpc, core::RpcResult};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

#[rpc(client)]
pub trait Rpc {
Expand All @@ -10,7 +10,7 @@ pub trait Rpc {
#[method(name = "bar")]
fn sync_method(&self) -> RpcResult<u16>;

#[subscription(name = "sub", item = String)]
#[subscription(name = "subscribe", item = String)]
fn sub(&self);
}

Expand Down
6 changes: 3 additions & 3 deletions proc-macros/tests/ui/correct/only_server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::net::SocketAddr;

use jsonrpsee::proc_macros::rpc;
use jsonrpsee::core::{async_trait, RpcResult};
use jsonrpsee::proc_macros::rpc;
use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder};

#[rpc(server)]
Expand All @@ -12,7 +12,7 @@ pub trait Rpc {
#[method(name = "bar")]
fn sync_method(&self) -> RpcResult<u16>;

#[subscription(name = "sub", item = String)]
#[subscription(name = "subscribe", item = String)]
fn sub(&self) -> RpcResult<()>;
}

Expand All @@ -39,7 +39,7 @@ pub async fn websocket_server() -> SocketAddr {
let addr = server.local_addr().unwrap();

server.start(RpcServerImpl.into_rpc()).unwrap();

addr
}

Expand Down
5 changes: 3 additions & 2 deletions proc-macros/tests/ui/correct/parse_angle_brackets.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use jsonrpsee::{proc_macros::rpc, core::RpcResult};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

fn main() {
#[rpc(server)]
pub trait Rpc {
#[subscription(
name = "submitAndWatchExtrinsic",
unsubscribe = "author_unwatchExtrinsic",
aliases = ["author_extrinsicUpdate"],
unsubscribe_aliases = ["author_unwatchExtrinsic"],
unsubscribe_aliases = ["author_unwatchExtrinsic2"],
// Arguments are being parsed the nearest comma,
// angle braces need to be accounted for manually.
item = TransactionStatus<Hash, BlockHash>,
Expand Down
4 changes: 2 additions & 2 deletions proc-macros/tests/ui/correct/rpc_deny_missing_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

#![deny(missing_docs)]

use jsonrpsee::proc_macros::rpc;
use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc;

#[rpc(client, server)]
pub trait ApiWithDocumentation {
Expand All @@ -12,7 +12,7 @@ pub trait ApiWithDocumentation {
async fn async_method(&self) -> RpcResult<u8>;

/// Subscription docs.
#[subscription(name = "sub", item = String)]
#[subscription(name = "sub", unsubscribe = "unsub", item = String)]
fn sub(&self) -> RpcResult<()>;
}

Expand Down
4 changes: 2 additions & 2 deletions proc-macros/tests/ui/incorrect/sub/sub_conflicting_alias.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use jsonrpsee::{proc_macros::rpc, core::RpcResult};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

#[rpc(client, server)]
pub trait DuplicatedSubAlias {
#[subscription(name = "alias", item = String, aliases = ["hello_is_goodbye"], unsubscribe_aliases = ["hello_is_goodbye"])]
#[subscription(name = "subscribeAlias", item = String, aliases = ["hello_is_goodbye"], unsubscribe_aliases = ["hello_is_goodbye"])]
fn async_method(&self) -> RpcResult<()>;
}

Expand Down
6 changes: 3 additions & 3 deletions proc-macros/tests/ui/incorrect/sub/sub_dup_name_override.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use jsonrpsee::{proc_macros::rpc, core::RpcResult};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

// Subscription method must not use the same override name.
#[rpc(client, server)]
pub trait DupOverride {
#[subscription(name = "one" => "override", item = u8)]
#[subscription(name = "subscribeOne" => "override", item = u8)]
fn one(&self) -> RpcResult<()>;
#[subscription(name = "two" => "override", item = u8)]
#[subscription(name = "subscribeTwo" => "override", item = u8)]
fn two(&self) -> RpcResult<()>;
}

Expand Down
4 changes: 2 additions & 2 deletions proc-macros/tests/ui/incorrect/sub/sub_name_override.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use jsonrpsee::{proc_macros::rpc, core::RpcResult};
use jsonrpsee::{core::RpcResult, proc_macros::rpc};

// Subscription method name conflict with notif override.
#[rpc(client, server)]
pub trait DupName {
#[subscription(name = "one" => "one", item = u8)]
#[subscription(name = "one" => "one", unsubscribe = "unsubscribeOne", item = u8)]
fn one(&self) -> RpcResult<()>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use jsonrpsee::proc_macros::rpc;
// Unsupported attribute field.
#[rpc(client, server)]
pub trait UnsupportedField {
#[subscription(name = "sub", item = u8, magic = true)]
#[subscription(name = "sub", unsubscribe = "unsub", item = u8, magic = true)]
fn sub(&self);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: Unknown argument `magic`, expected one of: `aliases`, `item`, `name`, `param_kind`, `unsubscribe_aliases`
--> $DIR/sub_unsupported_field.rs:6:42
error: Unknown argument `magic`, expected one of: `aliases`, `item`, `name`, `param_kind`, `unsubscribe`, `unsubscribe_aliases`
--> tests/ui/incorrect/sub/sub_unsupported_field.rs:6:65
|
6 | #[subscription(name = "sub", item = u8, magic = true)]
| ^^^^^
6 | #[subscription(name = "sub", unsubscribe = "unsub", item = u8, magic = true)]
| ^^^^^
6 changes: 3 additions & 3 deletions tests/tests/proc_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ mod rpc_impl {
#[method(name = "bar")]
fn sync_method(&self) -> RpcResult<u16>;

#[subscription(name = "sub", item = String)]
#[subscription(name = "sub", unsubscribe = "unsub", item = String)]
fn sub(&self) -> RpcResult<()>;

#[subscription(name = "echo", aliases = ["alias_echo"], item = u32)]
#[subscription(name = "echo", unsubscribe = "unsubscribe_echo", aliases = ["alias_echo"], item = u32)]
fn sub_with_params(&self, val: u32) -> RpcResult<()>;

#[method(name = "params")]
Expand Down Expand Up @@ -123,7 +123,7 @@ mod rpc_impl {
#[rpc(client, server, namespace = "generic_sub")]
pub trait OnlyGenericSubscription<Input, R> {
/// Get header of a relay chain block.
#[subscription(name = "sub", item = Vec<R>)]
#[subscription(name = "sub", unsubscribe = "unsub", item = Vec<R>)]
fn sub(&self, hash: Input) -> RpcResult<()>;
}

Expand Down