A Rust companion library for duplex-rpc (see there for more details about the RPC protocol and its motivation), a TypeScript duplex RPC framework. It lets you write WASI-based WebAssembly binaries in Rust that expose functions (including streaming) to a TypeScript host process over stdin/stdout.
RpcServer: a single-threaded, cooperative async event loop that reads incoming RPC messages from stdin and writes responses to stdout.CallContext: per-call handle passed to every handler, giving access to streaming primitives (stream_reader,stream_writer) and the ability to call back into the host (call_host).StreamReader/StreamWriter: async and sync helpers for consuming and producing streams over the RPC channel.
The underlying wire protocol (length-prefixed MessagePack frames) is defined and documented in the duplex-rpc TypeScript repository. This crate implements the same protocol so that Rust WASM binaries can participate as peers.
The following example exposes a single add function that takes two numbers
and returns their sum. When compiled to wasm32-wasip1, the resulting
.wasm binary can be spawned by a TypeScript host using duplex-rpc.
use duplex_rpc::{CallContext, RpcServer, rmpv};
fn main() {
let mut server = RpcServer::new();
server.register("add", handle_add);
server
.run(std::io::stdin(), std::io::stdout())
.expect("RPC server error");
}
async fn handle_add(
_ctx: CallContext,
params: Option<rmpv::Value>,
) -> Result<Option<rmpv::Value>, String> {
let args = params
.and_then(|v| if v.is_array() { Some(v) } else { None })
.ok_or("expected an array of arguments")?;
let arr = args.as_array().unwrap();
let a = arr.first().and_then(rmpv::Value::as_f64).unwrap_or(0.0);
let b = arr.get(1).and_then(rmpv::Value::as_f64).unwrap_or(0.0);
Ok(Some(rmpv::Value::F64(a + b)))
}Compile with:
cargo build --target wasm32-wasip1 --releaseconst child = spawn('wasmtime', ['run', 'path/to/duplex_rpc.wasm'], {
stdio: ['pipe', 'pipe', 'inherit'],
});
const transport = duplexFromSpawnedProcess(child);
using api = createApiClientFromDuplex(transport, stringLengthApi);
const result = await api.add(2, 3);
console.log(result); // 5
child.kill();