Generating Code

Single file

# Rust (default target)
vexilc codegen hello.vexil --target rust --output hello.rs

# TypeScript
vexilc codegen hello.vexil --target typescript --output hello.ts

# Go
vexilc codegen hello.vexil --target go --output hello.go

# Python
vexilc codegen hello.vexil --target python --output hello.py

Default target is rust. Output goes to stdout if --output is omitted.

Rust and TypeScript generated paths have broad cross-language byte-vector coverage. Generated Go and Python are verified against a representative shared wire matrix; verify application-specific schemas and environments separately.

Traits are emitted as structural contracts in every target. Portable trait functions become mutable instance methods:

  • Rust uses &mut self.
  • TypeScript keeps message interfaces and adds a MessageFields input type plus createMessage object factory. Decode routes through the factory so decoded values carry their methods.
  • Go uses pointer-receiver methods.
  • Python uses dataclass methods and emits static Protocol conformance proofs under TYPE_CHECKING.

Portable bodies support literals, parameters, immutable let bindings, self.field, receiver-field assignment, return, and unary/binary operators. Free calls, method calls, local reassignment, non-receiver assignment, and target identifier collisions fail code generation before output is produced. Impl bodies are generated source templates; they do not add runtime dispatch or wire metadata.

Multi-file project

For schemas with imports, use the build subcommand:

vexilc build root.vexil --include ./schemas --output ./generated --target rust

This resolves all imports, compiles in topological order, and generates one file per namespace. Project code generation resolves traits imported directly by name, through a unique wildcard, or through one explicit alias qualifier such as impl Contracts.Tagged<u64> for Event. A trait reachable only through another schema's imports is not re-exported: the implementing schema must import it explicitly. Imported impl declarations remain in their defining schema and are not copied into consumers.

Watch mode

Auto-rebuild on save:

vexilc watch root.vexil --include ./schemas --output ./generated --target typescript

Changes to any .vexil file in the watched directories trigger a rebuild with 200ms debounce.

Using generated code

Rust

Add vexil-runtime to your Cargo.toml:

[dependencies]
vexil-runtime = "0.5"
#![allow(unused)]
fn main() {
use vexil_runtime::{BitWriter, BitReader, Pack, Unpack};

let greeting = Greeting {
    name: "world".to_string(),
    message: "hello".to_string(),
    count: 42,
    _unknown: Vec::new(),
};

// Encode
let mut w = BitWriter::new();
greeting.pack(&mut w).unwrap();
let bytes = w.finish();

// Decode
let mut r = BitReader::new(&bytes);
let decoded = Greeting::unpack(&mut r).unwrap();
}

TypeScript

Install @vexil-lang/runtime:

npm install @vexil-lang/runtime
import { BitWriter, BitReader } from '@vexil-lang/runtime';
import { encodeGreeting, decodeGreeting } from './hello';

const w = new BitWriter();
encodeGreeting(
  { name: 'world', message: 'hello', count: 42, _unknown: new Uint8Array(0) },
  w,
);
const bytes = w.finish();

const r = new BitReader(bytes);
const decoded = decodeGreeting(r);

Go

import vexil "github.com/vexil-lang/vexil/packages/runtime-go"

greeting := &Greeting{
    Name:    "world",
    Message: "hello",
    Count:   42,
}

w := vexil.NewBitWriter()
greeting.Pack(w)
bytes := w.Finish()

r := vexil.NewBitReader(bytes)
var decoded Greeting
decoded.Unpack(r)

The versioned Go runtime module is available as github.com/vexil-lang/vexil/packages/runtime-go@v0.1.1. See the Go runtime page for installation details.

Python

Install the published runtime from PyPI:

python -m pip install vexil-runtime

When testing changes from this checkout, you can instead install ./packages/runtime-py into an isolated environment.

from hello import Greeting

encoded = Greeting(name="world", message="hello", count=42).encode()
decoded = Greeting.decode(encoded)
assert decoded.count == 42

See the support matrix before choosing a production target combination.