Automates the creation of WIT (WebAssembly Interface Type) files and RPC stubs for Hyperware processes.
# Install from the current directory
cargo install --path .
Run this tool before building your Hyperware project:
# Navigate to your project root
cd my-hyperware-project
# Run hyper-bindgen
hyper-bindgen
# The tool will:
# 1. Find all Rust files with hyperprocess implementations
# 2. Generate corresponding WIT files in the api/ directory
# 3. Create the caller-utils crate with RPC stubs
# Then build as normal
kit b # build
kit s # start (assuming you started a fakenode with `kit f`)
Hyper-Bindgen scans your codebase for Hyperware processes (identified by the #[hyperprocess]
macro) and performs two steps:
- WIT File Generation: Creates wit files with function signatures and downstream structs used either in the args or return value for all annotated functions
- Caller Utils Generation: Builds a helper crate with RPC stub functions
This allows you to call other process endpoints from a process through auto-generated async functions with proper type checking, rather than manually constructing JSON messages.
When run in a project, Hyper-Bindgen performs two main tasks:
- Scans for Rust projects with
package.metadata.component.package = "hyperware:process"
in Cargo.toml - Analyzes each project for
#[hyperprocess]
macro implementations - Extracts function signatures from methods annotated with
#[http]
,#[remote]
, or#[local]
- Generates WIT files in an
/api
directory with proper type conversions
- Creates a
caller-utils
crate containing RPC stub functions for easy inter-process communication - Updates the workspace Cargo.toml to include the new crate
- Adds the caller-utils dependency to relevant projects
Note: In the future, we should extend the
kit b
command to automatically execute hyper-bindgen beforehand, eliminating the need for a separate step.
For a Hyperware process with annotated functions:
#[hyperprocess(
name = "Async Requester",
wit_world = "async-app-template-dot-os-v0"
)]
impl AsyncRequesterState {
#[remote]
#[local]
fn increment_counter(&mut self, value: i32, name: String) -> f32 {
// Implementation...
0.0
}
}
Hyper-Bindgen will:
-
Generate a WIT file with:
interface async-requester { use standard.{address}; record increment-counter-signature-remote { target: address, value: s32, name: string, returning: f32, } record increment-counter-signature-local { target: address, value: s32, name: string, returning: f32, } }
-
Create a caller-utils crate with stub functions:
pub mod async_requester { use crate::*; /// Generated stub for `increment-counter` remote RPC call pub async fn increment_counter_remote_rpc(target: &Address, value: i32, name: String) -> SendResult<f32> { let request = json!({"IncrementCounter": (value, name)}); send::<f32>(&request, target, 30).await } /// Generated stub for `increment-counter` local RPC call pub async fn increment_counter_local_rpc(target: &Address, value: i32, name: String) -> SendResult<f32> { let request = json!({"IncrementCounter": (value, name)}); send::<f32>(&request, target, 30).await } }
With the generated stubs, you can call another process's endpoint like this:
use caller_utils::async_requester::increment_counter_remote_rpc;
use shared::receiver_address;
async fn my_function() {
let result = increment_counter_remote_rpc(&receiver_address(), 42, "test".to_string()).await;
match result {
SendResult::Success(value) => println!("Got result: {}", value),
SendResult::Error(err) => println!("Error: {}", err),
}
}
Instead of manually constructing JSON:
// Without hyper-bindgen (error-prone)
let request = json!({"IncrementCounter": (42, "test")});
let result = send::<f32>(&request, &receiver_address(), 30).await;
Under the hood, we still do the regular sending of data through messages. The body of the messages will always follow the RequestEnum
/ResponseEnum
with the variants being a CamelCase version of each defined function, and the inner value of those variants being the arguments of the functions defined in the hyperware macro functions.