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

use std::net::SocketAddr;

use futures_channel::oneshot;
use jsonrpsee::{ws_client::*, ws_server::WsServerBuilder};

mod rpc_impl {
	use jsonrpsee::{proc_macros::rpc, types::async_trait, 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;

		#[method(name = "bar")]
		fn sync_method(&self) -> u16;

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

		#[subscription(name = "echo", unsub = "no_more_echo", item = u32)]
		fn sub_with_params(&self, val: u32);
	}

	pub struct RpcServerImpl;

	#[async_trait]
	impl RpcServer for RpcServerImpl {
		async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 {
			42u16
		}

		fn sync_method(&self) -> u16 {
			10u16
		}

		fn sub(&self, mut sink: SubscriptionSink) {
			sink.send(&"Response_A").unwrap();
			sink.send(&"Response_B").unwrap();
		}

		fn sub_with_params(&self, mut sink: SubscriptionSink, val: u32) {
			sink.send(&val).unwrap();
			sink.send(&val).unwrap();
		}
	}
}

// Use generated implementations of server and client.
use rpc_impl::{RpcClient, RpcServer, RpcServerImpl};

pub async fn websocket_server() -> SocketAddr {
	let (server_started_tx, server_started_rx) = oneshot::channel();

	std::thread::spawn(move || {
		let rt = tokio::runtime::Runtime::new().unwrap();
		let mut server = rt.block_on(WsServerBuilder::default().build("127.0.0.1:0")).unwrap();
		server.register_module(RpcServerImpl.into_rpc().unwrap()).unwrap();

		rt.block_on(async move {
			server_started_tx.send(server.local_addr().unwrap()).unwrap();

			server.start().await
		});
	});

	server_started_rx.await.unwrap()
}

#[tokio::test]
async fn proc_macros_generic_ws_client_api() {
	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);

	// Sub without params
	let mut sub = client.sub().await.unwrap();
	let first_recv = sub.next().await.unwrap();
	assert_eq!(first_recv, Some("Response_A".to_string()));
	let second_recv = sub.next().await.unwrap();
	assert_eq!(second_recv, Some("Response_B".to_string()));

	// Sub with params
	let mut sub = client.sub_with_params(42).await.unwrap();
	let first_recv = sub.next().await.unwrap();
	assert_eq!(first_recv, Some(42));
	let second_recv = sub.next().await.unwrap();
	assert_eq!(second_recv, Some(42));
}