8 releases (4 breaking)

Uses new Rust 2024

new 0.5.0 May 15, 2026
0.4.0 Apr 15, 2026
0.3.1 Mar 30, 2026
0.2.2 Mar 28, 2026
0.0.0 Mar 23, 2026

#118 in #rpc

MIT/Apache

620KB
14K SLoC

Code generation for vox RPC bindings across multiple languages.

This Is Where Code Generation Actually Happens

While vox-macros parses your service traits and emits metadata, this crate consumes that metadata and generates actual protocol implementations for:

  • TypeScript — Browser and Node.js clients
  • Swift — iOS/macOS clients
  • Go — Server and client implementations
  • Java — Android and server implementations
  • Python — Client bindings
  • Rust — Extended codegen beyond what the proc macro provides

Usage: In Your build.rs

// In your service crate's build.rs
use my_service::calculator_service_detail;

fn main() {
    let detail = calculator_service_detail();

    // Generate TypeScript client
    let ts_code = vox_codegen::targets::typescript::generate(&detail);
    std::fs::write("generated/calculator.ts", ts_code).unwrap();

    // Generate Go server
    let go_code = vox_codegen::targets::go::generate(&detail);
    std::fs::write("generated/calculator.go", go_code).unwrap();
}

The Pipeline

#[service] trait     →    ServiceDescriptor    →    vox-codegen    →    .ts, .go, .swift, ...
  (your code)            (runtime metadata)     (build script)       (generated code)

Why Build Scripts? (The Technical Reason)

Code generation happens in build scripts (not proc macros) because proc macros cannot see into the type system.

When a proc macro sees Tx<String> in a method signature, it sees tokens — it has no idea if Tx refers to vox::channel::Tx or some user-defined type. It cannot resolve type aliases, follow generic parameters, or inspect nested types.

But here, with facet::Shape, we have full type introspection:

// We can identify vox's Tx vs user-defined types
let shape = <Tx<String> as facet::Facet>::SHAPE;

// We can traverse nested types like Result<Vec<Tx<T>>, Error>
// and find the Tx buried inside

This is why validation happens here:

  • Is this actually vox's Tx/Rx channel type?
  • Are channel types incorrectly used in error positions?
  • What serialization does this nested type require?

Additional benefits of build scripts:

  1. File I/O — Build scripts can write files; proc macros cannot
  2. Configuration — Build scripts can read config files, env vars, etc.
  3. Flexibility — Different projects can generate different subsets of bindings

vox-codegen

Language binding generator for Vox service descriptors.

Role in the Vox stack

vox-codegen bridges Rust-defined schemas to non-Rust clients/servers above the RPC surface.

What this crate provides

  • TypeScript and Swift code generation targets
  • Rendering of service descriptors into client/server scaffolding

Fits with

  • vox service definitions and generated descriptors
  • vox-hash and vox-types for shared protocol identity and type model

Part of the Vox workspace: https://github.com/bearcove/vox

Dependencies

~9–12MB
~229K SLoC