A declaration model and renderer for generating Kotlin source code.
Declarations (files, classes, functions, properties, parameters, types,
annotations) are typed model values (KtFile, KtClass, KtFun, KtType,
…); imports and formatting derive from the model automatically via
ImportSet. Statement bodies stay raw Kotlin text, structured through the
indentation-aware KtCode builder rather than modeled as a full expression AST.
You build a KtFile out of typed pieces, and the renderer produces
formatted, import-complete Kotlin source text plus a file-writer that lays
the result out on disk.
Two runnable examples, both checked against stored output by
tests/examples.rs so they cannot rot:
cargo run --example showcase # every construct the model can emit
cargo run --example invalid # a broken model, and what the validator saysshowcase is a small but complete generator — it builds KtFile fragments,
merges them so each package collapses to one file, and renders classes,
objects, enums, data and value classes, sealed interfaces, fun interfaces,
type aliases, extension functions, properties with accessors and delegates,
external natives, and raw blocks. invalid shows the two ways a mistake surfaces: as a diagnostic
from the validator, or as a builder that refuses to construct the value at all.
To accept an intended change to either output:
UPDATE_GOLDEN=1 cargo test --test examples # then review the diffA program builds the model, so the mistakes are program mistakes: a name
derived from a Rust field that happens to be object, two generated classes
that mangle to the same name, a branch that built an object where it meant a
data class. Without a check, those first surface when the Kotlin compiler
runs — often in a different build, against a generated file that gives no hint
about what produced it.
Two things address that.
Bad shapes are mostly unbuildable. The kinds carry their own data, so an
object has nowhere to put constructor parameters, a value class holds
exactly one property, a data class cannot have zero, a companion object
cannot be a top-level declaration, a class cannot construct two superclasses,
a fun interface method cannot have a body, and external cannot be combined
with one.
What is left is validated. KtFile::validate() walks the file, every class
body and every companion body and reports what the model can prove is wrong:
names that are not legal Kotlin identifiers, redeclarations (per scope, with
Kotlin's separate type / value / function namespaces, so real overloads pass),
and the shapes the types could not rule out. merge_files and write_files
run it and refuse to write.
It is deliberately not a Kotlin compiler: no type resolution, no parsing of
body text, and no check that could fire on correct output. ValidationPolicy
sets any check to error, warning or off — ValidationPolicy::warn_all() is the
way to adopt validation in a generator that already produces output.
Generated names come from somewhere else — a Rust field, a C symbol, a JSON
key — and some are never legal Kotlin. is_valid_kotlin_ident,
mangle_kotlin_ident (my-name → my_name, object → object_) and
escape_kotlin_ident (`object`, keeping the name instead of changing it)
are available for that, along with their package-path equivalents.
This crate was made for the prebindgen-jni library, which generates JNI bindings for Rust libraries. The existing kotlin-poet-rs crate was not used for Not-Invented-Here reasons and because full control over the code generator was needed.
This crate will be supported as long as prebindgen-jni is alive.
Early, dependency-free, and intentionally minimal. No macros, no derive, no external crates — just a plain declaration model, a renderer, and a validator.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Publishing is automated from GitHub Actions. See RELEASING.md for the release process and CHANGELOG.md for release notes.