Importing Crates

RustScript gives you the entire Rust crate ecosystem through TypeScript import syntax. No npm install. No package.json. Write an import, and the compiler handles the rest.

How it works

import { Router, serve } from "axum";

When the compiler sees this:

  1. It recognizes "axum" as a Rust crate
  2. It merges axum = "..." into the Cargo.toml in your project root
  3. It emits use axum::{Router, serve}; in the generated Rust
  4. Cargo downloads and compiles the crate on first build

You write one line. The compiler figures out the dependency, the version, and the use declaration.

Submodule imports

Slash-separated paths map to Rust module paths:

import { get, post } from "axum/routing";
// → use axum::routing::{get, post};

import { TcpListener } from "tokio/net";
// → use tokio::net::TcpListener;

import { Serialize, Deserialize } from "serde";
// → use serde::{Serialize, Deserialize};

Auto-dependency detection

The compiler inspects every import in your .rts file. Known crates get pinned to compatible versions. For example, importing from "axum" adds axum = "0.7" with the right feature flags.

Known crate patterns include:

  • axum -- web framework
  • tokio -- async runtime (with appropriate features)
  • serde / serde_json -- serialization
  • reqwest -- HTTP client
  • clap -- CLI argument parsing

Unknown crate names get the latest version from crates.io.

The generated Cargo.toml

The compiler generates and maintains Cargo.toml in your project root. It is a standard Cargo manifest -- commit it to git.

The compiler merges rather than overwrites: the [dependencies] section is rebuilt from your imports on every compile, but any other edits you make (pinned versions, feature flags, [profile] sections) are preserved.

Example generated Cargo.toml:

[package]
name = "my-api"
edition = "2021"

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Example: web server

import { Router, serve } from "axum";
import { get } from "axum/routing";
import { TcpListener } from "tokio/net";

async function hello(): string {
  return "Hello from RustScript!";
}

async function main() {
  const app = new Router().route("/", get(hello));
  const listener = await TcpListener.bind("0.0.0.0:3000");
  console.log("Listening on http://0.0.0.0:3000");
  await serve(listener, app);
}

Three imports. Three crates auto-detected. A running web server.

Example: HTTP client

import { get } from "reqwest";

type Post = {
  userId: i64,
  id: i64,
  title: string,
  body: string,
} derives Deserialize

async function main() {
  const response = await get("https://jsonplaceholder.typicode.com/posts/1");
  const post: Post = await response.json();
  console.log(post.title);
}

Tips

  • Read the Rust crate docs. RustScript imports map directly to Rust's use paths. The crate's Rust documentation tells you what's available.
  • Use Type Mapping to translate Rust types back to RustScript when reading crate docs.
  • Feature flags are handled automatically for known crates. For unknown crates, you may need to check that the right features are enabled.