3 releases

0.0.3 Oct 14, 2024
0.0.2 Oct 11, 2024
0.0.1 Jan 28, 2024

#29 in #rpc-service

Download history 14/week @ 2024-07-24 4/week @ 2024-07-31 1/week @ 2024-09-18 4/week @ 2024-09-25 2/week @ 2024-10-02 308/week @ 2024-10-09 50/week @ 2024-10-16 5/week @ 2024-10-23

363 downloads per month
Used in web-rpc

MIT license

32KB
645 lines

CI Crates.io api-docs MIT licensed

web-rpc

web-rpc is a crate for executing RPCs between browsing contexts, web workers, and channels. Similar to Google's tarpc, this crate allows you to define your RPC in code using trait syntax. This trait is consumed by a service macro, which will generate everything that you need to implement RPC. Two notable features of this crate are that it supports bidirectional RPC over a single channel (e.g., between a Worker and a DedicatedWorkerGlobalScope) and posting/transferring Javascript types (e.g., OffscreenCanvas). The following is a simple example, see the crate documentation for a more complete explaination and more advanced examples.

The following code generates the RPC components using an attribute macro applied to a trait. It is recommended to put this RPC definition into some sort of shared crate that your modules can both access.

#[web_rpc::service]
pub trait Calculator {
    fn add(left: u32, right: u32) -> u32;
}

The code above will generate CalculatorClient, CalculatorService, and a new trait Calculator that you can use to implement a calculator as follows:

struct CalculatorServiceImpl;

impl Calculator for CalculatorServiceImpl {
    fn add(&self, left: u32, right: u32) -> u32 {
        left + right
    }
}

In the following example, we will use MessageChannel and MessagePort since they are easy to test and demonstrate the use of this crate inside a single module. A more interesting example however, is to use this crate to communicate between two browsing contexts or a Worker and a DedicatedWorkerGlobalScope. The following code defines the server:

let channel = web_sys::MessageChannel::new();
// note that interface::new is async and that both interfaces need to be polled in order to establish the connection between them
let (server_interface, client_interface) = futures_util::future::join(
    web_rpc::Interface::new(channel.port1()),
    web_rpc::Interface::new(channel.port2()),
).await;
// create a server with the first port
let server = web_rpc::Builder::new(server_interface)
    .with_service::<CalculatorService<_>>(CalculatorServiceImpl)
    .build();
// spawn the server
wasm_bindgen_futures::spawn_local(server);

To create a client:

// create a client using the second interface from above
let client = web_rpc::Builder::new(client_interface)
    .with_client::<CalculatorClient>()
    .build();
/* call `add` */
assert_eq!(client.add(41, 1).await, 42);

For more advanced examples, check out the crate documentation.

Dependencies

~1.5MB
~37K SLoC