Skip to content

dannote/rustq

Repository files navigation

RustQ

Hex.pm Documentation

Write native Elixir in Elixir. RustQ turns typed, Elixir-shaped code into readable Rust and can generate, compile, and load a complete Rustler NIF without checked-in bridge Rust.

defmodule MyApp.Native do
  use RustQ.Native, crates: [crc32fast: "1"]

  alias RustQ.Type, as: R

  @type point :: %{required(:x) => float(), required(:y) => float()}

  @spec checksum(String.t()) :: R.u32()
  defnif checksum(value), do: Crc32fast.hash(value.as_bytes())

  @spec scale(point(), float()) :: point()
  defnif scale(point, factor) do
    %{x: point.x * factor, y: point.y * factor}
  end
end

That module is the bridge. RustQ derives the Cargo crate, Rustler entrypoints, Elixir stubs, boundary codecs, initialization, build, and native loading. There is no .rs file, Cargo.toml, atom registry, or duplicated signature list to maintain.

Typed Elixir in, Rust out

defrust uses the Elixir typespec as the Rust signature source of truth, then lowers Elixir semantics rather than substituting text:

defmodule MyApp.Generated do
  use RustQ.Meta

  @spec sum_squares([integer()]) :: integer()
  defrust sum_squares([]), do: 0
  defrust sum_squares([head | tail]), do: head * head + sum_squares(tail)
end

The list and integer types become Vec<i64> and i64; clauses and list patterns become a Rust match:

fn sum_squares(arg1: Vec<i64>) -> i64 {
    match arg1.as_slice() {
        [] => 0,
        [head, tail @ ..] => head * head + sum_squares(tail.to_vec()),
    }
}

RustQ.Type extends normal typespecs with Rust-specific precision such as references, fixed-width numbers, lifetimes, slices, options, results, and NifResult. The same lowerer powers defnif, which additionally derives the BEAM boundary and Rustler attribute. It understands guards, structs, typed maps, comprehensions, closures, and a semantics-preserving subset of Kernel, Enum, List, Map, String, Tuple, and Range.

Why RustQ

Native Elixir libraries usually repeat the same facts across Elixir stubs, Rust functions, Rustler codecs, Cargo configuration, and code generators. RustQ keeps those facts structural and close to their source:

  • @spec and @type drive Rust signatures and ABI codecs.
  • defnif, defrust, and defrustp use ordinary Elixir syntax, pattern matching, guards, recursion, comprehensions, and selected standard-library calls.
  • Real Rust source and Cargo packages provide callable and type metadata, so RustQ can infer borrows and fallible ? propagation instead of requiring wrapper functions.
  • RustQ AST, real Rust templates, and checked generation scale from one NIF to large schema-driven native libraries without assembling Rust strings.
  • Scheduling, unsafe operations, resources, lossy conversion, dependencies, linking, and release targets remain explicit policy.

RustQ is for bridge and generator code. Domain-heavy parsers, renderers, and algorithms can stay in Rust while RustQ removes the repetitive boundary glue.

Installation

def deps do
  [
    {:rustq, "~> 1.0.0-rc.3", runtime: false}
  ]
end

Projects that only run checked-in generators during development may use only: [:dev, :test]. Rust and Cargo must be available wherever native modules or generators compile.

Choose the right workflow

You want to… Start with
Build and load a NIF without handwritten bridge Rust use RustQ.Native and defnif
Generate Rust helpers from typed Elixir use RustQ.Meta and defrust
Integrate an existing or precompiled crate RustQ.Native, build: false, load: false
Read callable metadata from real Rust rust_sources, rust_packages, and RustQ.Syn
Generate declarations from schemas RustQ AST builders and ordinary Elixir macros
Keep generated Rust checked in rustq.exs and mix rustq.gen --check
Generate around substantial handwritten Rust parseable .rs templates and structural splices

Rusty-Elixir is intentionally Elixir-shaped rather than fake Rust syntax. When a construct cannot be expressed honestly in Elixir, use RustQ AST. Raw Rust tokens are the final, local escape hatch—not the default authoring style.

Documentation

Agent skill

RustQ ships SKILL.md, an operational guide for coding agents that work on RustQ-powered bridges and generators. Give it to an agent before it writes generated Rust; it captures the inference-first, AST-first authoring order and the important escape boundaries.

Development

mix deps.get
mix ci

License

MIT

About

Rust template quasiquoting and code generation for Elixir

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages