Skip to main content

Crate facet_generate

Crate facet_generate 

Source
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 the facet crate) and builds a language-neutral Registry: a flat map from qualified type names to their ContainerFormat descriptions.
  • 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-attrs

The #[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(&registry)?;

// Kotlin package with Bincode serialization
kotlin::Installer::new("com.example", out_dir.join("kotlin"))
    .plugin(BincodePlugin)
    .generate(&registry)?;

// C# project with Bincode serialization
csharp::Installer::new("Example", out_dir.join("csharp"))
    .plugin(BincodePlugin)
    .generate(&registry)?;

// TypeScript with Bincode serialization
typescript::Installer::new("my-package", out_dir.join("typescript"))
    .plugin(BincodePlugin)
    .generate(&registry)?;

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

AttributeEffect
#[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 Registry into 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§

reflect
Reflects one or more types into a Registry, recursively capturing all reachable types.

Enums§

Attr
Extension attributes for facet_generate code generation.

Type Aliases§

Registry
The registry of reflected types — a flat map from qualified type names to their container formats.
Result