basic.rs 4.29 KiB
Newer Older
Igor Aleksanov's avatar
Igor Aleksanov committed
//! Example of using proc macro to generate working client and server.

Maciej Hirsz's avatar
Maciej Hirsz committed
use std::net::SocketAddr;
use jsonrpsee::core::{async_trait, client::ClientT, RpcResult};
Maciej Hirsz's avatar
Maciej Hirsz committed
use jsonrpsee::proc_macros::rpc;
use jsonrpsee::rpc_params;
use jsonrpsee::ws_client::*;
use jsonrpsee::ws_server::{PendingSubscription, WsServerBuilder};
Igor Aleksanov's avatar
Igor Aleksanov committed
#[rpc(client, server, namespace = "foo")]
pub trait Rpc {
	#[method(name = "foo", aliases = ["fooAlias", "Other"])]
David's avatar
David committed
	async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult<u16>;
	#[method(name = "optional_params")]
David's avatar
David committed
	async fn optional_params(&self, a: std::option::Option<u8>, b: String) -> RpcResult<bool>;
David's avatar
David committed
	async fn optional_param(&self, a: Option<u8>) -> RpcResult<bool>;
David's avatar
David committed
	async fn array_params(&self, items: Vec<u64>) -> RpcResult<u64>;
Igor Aleksanov's avatar
Igor Aleksanov committed
	#[method(name = "bar")]
David's avatar
David committed
	fn sync_method(&self) -> RpcResult<u16>;
	#[subscription(name = "subscribe", item = String)]
	#[subscription(name = "echo", unsubscribe = "unsubscribeEcho", aliases = ["ECHO"], item = u32, unsubscribe_aliases = ["NotInterested", "listenNoMore"])]
	fn sub_with_params(&self, val: u32);

	// This will send data to subscribers with the `method` field in the JSON payload set to `foo_subscribe_override`
	// because it's in the `foo` namespace.
	#[subscription(name = "subscribe_method" => "subscribe_override", item = u32)]
	fn sub_with_override_notif_method(&self);
Igor Aleksanov's avatar
Igor Aleksanov committed
}

pub struct RpcServerImpl;

#[async_trait]
impl RpcServer for RpcServerImpl {
David's avatar
David committed
	async fn async_method(&self, _param_a: u8, _param_b: String) -> RpcResult<u16> {
David's avatar
David committed
	async fn optional_params(&self, a: core::option::Option<u8>, _b: String) -> RpcResult<bool> {
		let res = if a.is_some() { true } else { false };
		Ok(res)
	}

David's avatar
David committed
	async fn optional_param(&self, a: Option<u8>) -> RpcResult<bool> {
		let res = if a.is_some() { true } else { false };
		Ok(res)
	}

David's avatar
David committed
	async fn array_params(&self, items: Vec<u64>) -> RpcResult<u64> {
David's avatar
David committed
	fn sync_method(&self) -> RpcResult<u16> {
	fn sub(&self, pending: PendingSubscription) {
Niklas Adolfsson's avatar
Niklas Adolfsson committed
		tokio::spawn(async move {
			let mut sink = match pending.accept().await {
				Some(sink) => sink,
				_ => return,
			};
			let _ = sink.send(&"Response_A");
			let _ = sink.send(&"Response_B");
		});
	fn sub_with_params(&self, pending: PendingSubscription, val: u32) {
Niklas Adolfsson's avatar
Niklas Adolfsson committed
		tokio::spawn(async move {
			let mut sink = match pending.accept().await {
				Some(sink) => sink,
				_ => return,
			};
			let _ = sink.send(&val);
			let _ = sink.send(&val);
		});
Igor Aleksanov's avatar
Igor Aleksanov committed
	}
	fn sub_with_override_notif_method(&self, pending: PendingSubscription) {
Niklas Adolfsson's avatar
Niklas Adolfsson committed
		tokio::spawn(async move {
			if let Some(mut sink) = pending.accept().await {
				let _ = sink.send(&1);
			}
		});
Igor Aleksanov's avatar
Igor Aleksanov committed
}

pub async fn websocket_server() -> SocketAddr {
Maciej Hirsz's avatar
Maciej Hirsz committed
	let server = WsServerBuilder::default().build("127.0.0.1:0").await.unwrap();
	let addr = server.local_addr().unwrap();
Maciej Hirsz's avatar
Maciej Hirsz committed
	server.start(RpcServerImpl.into_rpc()).unwrap();
Maciej Hirsz's avatar
Maciej Hirsz committed
	addr
Igor Aleksanov's avatar
Igor Aleksanov committed
}

#[tokio::main]
async fn main() {
	let server_addr = websocket_server().await;
	let server_url = format!("ws://{}", server_addr);
	let client = WsClientBuilder::default().build(&server_url).await.unwrap();

	assert_eq!(client.async_method(10, "a".into()).await.unwrap(), 42);
	assert_eq!(client.sync_method().await.unwrap(), 10);
	assert_eq!(client.optional_params(None, "a".into()).await.unwrap(), false);
	assert_eq!(client.optional_params(Some(1), "a".into()).await.unwrap(), true);

	assert_eq!(client.array_params(vec![1]).await.unwrap(), 1);
	assert_eq!(client.request::<u64>("foo_array_params", rpc_params![Vec::<u64>::new()]).await.unwrap(), 0);

	assert_eq!(client.request::<bool>("foo_optional_param", rpc_params![]).await.unwrap(), false);
	assert_eq!(client.request::<bool>("foo_optional_param", None).await.unwrap(), false);
	assert_eq!(client.request::<bool>("foo_optional_param", rpc_params![1]).await.unwrap(), true);
Igor Aleksanov's avatar
Igor Aleksanov committed
	let mut sub = client.sub().await.unwrap();
	let first_recv = sub.next().await.transpose().unwrap();
Igor Aleksanov's avatar
Igor Aleksanov committed
	assert_eq!(first_recv, Some("Response_A".to_string()));
	let second_recv = sub.next().await.transpose().unwrap();
Igor Aleksanov's avatar
Igor Aleksanov committed
	assert_eq!(second_recv, Some("Response_B".to_string()));

	let mut sub = client.sub_with_override_notif_method().await.unwrap();
	let recv = sub.next().await.transpose().unwrap();
Igor Aleksanov's avatar
Igor Aleksanov committed
}