Expand description
Generates idiomatic source code in Swift, Kotlin, TypeScript, and C# from Rust types
annotated with #[derive(Facet)].
When Rust is the source of truth for a data model shared across platforms (e.g. a mobile app talking to a Rust core over FFI), every target language needs matching type definitions — and, optionally, serialization code to move data across the boundary. Writing these by hand is tedious and error-prone; this crate automates it.
Optionally, when a plugin such as BincodePlugin or
JsonPlugin is configured, the generated code includes
serialization for that format and the runtime library it needs is installed alongside the
generated code.
§Modules
reflection— walks Rust type metadata (via thefacetcrate) and builds a language-neutralRegistry: a flat map from qualified type names to theirContainerFormatdescriptions.generation— transforms a registry into source code for a target language. Each language (kotlin,csharp,swift,typescript) lives behind a feature flag and follows a three-layer pipeline: Installer (project scaffolding and manifests) → Generator (file-level output with imports and namespaces) → Emitter (per-type code emission).
§Getting Started
Add the crates to your project:
cargo add facet facet_generate facet-generate-attrsThe #[facet(fg::…)] attributes expand to paths in facet_generate_attrs, so a crate that
uses them must depend on facet-generate-attrs directly.
§1. Annotate your types
Derive facet::Facet on every type you want to share across language boundaries.
Aliasing this crate as fg keeps attribute paths short:
use facet::Facet;
use facet_generate as fg;
#[derive(Facet)]
#[repr(C)]
enum HttpResult {
Ok(HttpResponse),
Err(HttpError),
}
#[derive(Facet)]
struct HttpResponse {
status: u16,
headers: Vec<HttpHeader>,
#[facet(fg::bytes)] // Vec<u8> → native byte-array type
body: Vec<u8>,
}
#[derive(Facet)]
struct HttpHeader {
name: String,
value: String,
}You only need to register root types — all referenced types are collected transitively.
§2. Build a Registry
use facet_generate::reflection::RegistryBuilder;
let registry = RegistryBuilder::new()
.add_type::<HttpResult>()?
.build()?;§3. Generate code
Pass the Registry to a language-specific installer, optionally add plugins for
serialization support, and call generate():
use facet_generate::generation::{bincode::BincodePlugin, csharp, kotlin, swift, typescript};
// Swift package with Bincode serialization
swift::Installer::new("MyPackage", out_dir.join("swift"))
.plugin(BincodePlugin)
.generate(®istry)?;
// Kotlin package with Bincode serialization
kotlin::Installer::new("com.example", out_dir.join("kotlin"))
.plugin(BincodePlugin)
.generate(®istry)?;
// C# project with Bincode serialization
csharp::Installer::new("Example", out_dir.join("csharp"))
.plugin(BincodePlugin)
.generate(®istry)?;
// TypeScript with Bincode serialization
typescript::Installer::new("my-package", out_dir.join("typescript"))
.plugin(BincodePlugin)
.generate(®istry)?;Each installer writes a project to its directory — type definitions plus, when a
serialization plugin is configured, the appropriate runtime. The Swift, C# and TypeScript
projects build as they are written; the Kotlin sources need moving into src/main/kotlin
first (see the Kotlin guide). Omit .plugin(...) to generate plain type
definitions without any serialization code.
§Key attributes
| Attribute | Effect |
|---|---|
#[facet(fg::bytes)] | Emit Vec<u8> / &[u8] / [u8; N] / Bytes as a native byte-array type ([UInt8], Bytes, bytes, byte[]) |
#[facet(fg::namespace = "ns")] | Group a type, transitively, into a named namespace, emitted as a separate module |
#[facet(fg::namespace)] | Group a type, transitively, into the ROOT namespace |
#[facet(rename = "Name")] | Override the generated name of a type, field, or variant |
#[facet(rename_all = "camelCase")] | Apply a naming convention across all fields or variants. Options are PascalCase, camelCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, SCREAMING-KEBAB-CASE |
#[facet(skip)] | Exclude a field or variant from the generated output |
#[facet(opaque)] | Exclude a field from the generated output without reflecting its type, as for a type that can’t be generated |
#[facet(transparent)] | Unwrap a newtype wrapper in the generated output |
§Guide
The guide module goes further than this page: why this crate exists and how it compares
with other tools, the Rust types it supports in each language, a page per target language,
the serialization formats, and writing your own plugin.
Modules§
- error
- generation
- Code generation — transforms a
Registryinto source code. - guide
- Guides to using
facet_generate. These modules hold documentation only. Read these in order if you’re new; each one stands on its own if you’re not. - reflection
- Type reflection engine — extracts Rust type metadata into a language-neutral
Registry.
Macros§
Enums§
- Attr
- Extension attributes for facet_generate code generation.