A RustQ consumer should make the authored bridge easier to understand than the Rust it replaces. The generator is part of the product architecture, not a bag of build scripts.
Keep rustq.exs boring
Use rustq.exs as a composition root. Put domain metadata and build functions in
ordinary Elixir modules, then iterate over one target manifest.
use RustQ.Config
require_file("lib/my_app/codegen/atoms.ex")
require_file("lib/my_app/codegen/nifs.ex")
require_file("lib/my_app/codegen/targets.ex")
MyApp.Codegen.Targets.all()
|> Enum.map(fn {name, target} ->
generate name, Keyword.fetch!(target, :path) do
build(Keyword.fetch!(target, :build))
end
end)
|> List.last()Prefer this over large inline encoder tables, source parsing pipelines, and
manual implementation lookup in rustq.exs.
Use consistent layers
A useful naming convention is:
MyApp.Codegen.*for schemas, manifests, and orchestrationMyApp.Codegen.Rusty.*fordefrustimplementation modulesMyApp.Codegen.Rust.*for Rust AST composition and renderingMyApp.Nativefor the runtime Rustler loaderMyApp.Native.GeneratedStubsfor generated fallback functions
Use _impl for handwritten Rust implementation functions behind generated NIF
wrappers. Avoid _nif on exported names when the surrounding Native module
already supplies that context.
Generated Rust files should use predictable responsibility-based names such as
generated_atoms.rs, generated_nifs.rs, generated_term_encoders.rs, and
generated_types.rs.
Prefer ordinary manifests
Use lists, maps, structs, functions, and normal Elixir modules before creating a consumer-specific DSL. A manifest should own policy that cannot be inferred: which functions are exported, scheduling, semantic atom renames, and intentional field projections.
Do not repeat signatures, arities, lifetimes, or field types when RustQ can read them from Rust source, Rusty-Elixir specs, schemas, or generated AST metadata.
Keep metadata near its owner
Long lists of generated function names should live beside the functions they select. Shared defaults should have one owner. Generated atom keys should come from source references or normalized encoder/schema metadata, with only true identifier-to-value renames declared manually.
An explicit export-name list is useful policy. A second argument/return registry for those exports is duplication.
Optimize for inspection
Generated output should be split by responsibility when practical. A small root Rust module that includes generated atoms, types, wrappers, and domain helpers is easier to inspect than one monolithic generated file.
Do not abstract merely to reduce line count. Repetition can be valuable when it keeps public Elixir typespecs concrete and readable. Remove repetition when it creates two sources of truth or forces consumers to understand RustQ's internal manifest representation.