a Rust CLI tool called rsh, a REPL-like development shell for Rust projects.
From this repository:
cargo testFrom this repository:
cargo buildThis produces the binary at target/debug/rsh.
From the root of a single-crate Cargo project where you want to experiment:
/path/to/rsh/target/debug/rshrshexpects to run in a directory that contains aCargo.toml.- It will create and overwrite
src/bin/__rsh.rsin that project.
The following meta-commands are recognized only when typed as the first line of a new block (at rsh> ):
-
:quit/:q→ exit thershsession. -
:reset→ clear PREAMBLE and BODY buffers and reset to sync mode. -
:show→ print the current PREAMBLE, BODY, and mode. -
:delete <preamble|body> <index...>→ delete one or more lines from the PREAMBLE or BODY by index.- Example:
:delete preamble 0 2 3removes indices0,2, and3from the PREAMBLE buffer. - Indices are space-separated zero-based line numbers as shown by
:show. - Deletion is atomic: if any index is invalid for the chosen buffer, no lines are deleted and an error is printed.
- Example:
Any other line (including lines that later fail to compile) is treated as Rust code and appended to PREAMBLE or BODY based on the prefix rules.
- Allow developers to iteratively type Rust code in a terminal.
- Each input block regenerates a temporary Rust binary and runs it using Cargo.
- The interaction rhythm should feel like a Python REPL, but execution remains honest to Rust’s compilation model.
- The tool runs inside an existing Cargo project (single crate).
- It uses the project’s existing Cargo environment:
Cargo.tomlCargo.locktarget/build cache
- It does not:
- modify
Cargo.toml - add dependencies or features
- change toolchains
- touch
src/main.rs
- modify
- It generates and overwrites:
src/bin/__rsh.rs
- It invokes Cargo as:
cargo run --bin __rshusing thecargoexecutable found inPATH.
- Prompt:
rsh>for the first line of a block....>for continuation lines of the same block.
- Multi-line input:
- Type as many lines as you like.
- A blank line (pressing Enter on an empty line) ends the block and triggers execution.
- Blank lines before you start a block are ignored.
- Execution per block:
- The block is classified into PREAMBLE and BODY lines.
src/bin/__rsh.rsis regenerated from scratch.cargo run --bin __rshis invoked.stdoutandstderrfrom the run are printed verbatim.
rsh maintains two text buffers in memory:
- PREAMBLE
- Lines that begin with module-scope constructs:
use,mod,extern crate- attributes:
#![...]and#[...] struct,enum,type,trait,impl,fnconst,static
- Classification is prefix-based only; no AST parsing.
- Lines that begin with module-scope constructs:
- BODY
- All other non-empty lines.
- Typically executable statements (
let, function calls,println!, etc.).
These buffers persist for the duration of the rsh session and are completely regenerated into src/bin/__rsh.rs on each execution.
-
Sync mode (default):
- Prepend all PREAMBLE lines at module scope.
- Generate:
fn __rsh_session() -> Result<(), Box<dyn std::error::Error>> { // BODY lines, indented Ok(()) } fn main() { if let Err(e) = __rsh_session() { eprintln!("{}", e); } }
- The
?operator is allowed naturally inside__rsh_session.
-
Async mode:
- Prepend all PREAMBLE lines at module scope.
- Generate:
async fn __rsh_session() -> Result<(), Box<dyn std::error::Error>> { // BODY lines, indented Ok(()) }
- Wrap with runtime-specific main:
- Tokio:
#[tokio::main] async fn main() { if let Err(e) = __rsh_session().await { eprintln!("{}", e); } }
- async-std:
#[async_std::main] async fn main() { if let Err(e) = __rsh_session().await { eprintln!("{}", e); } }
- smol:
fn main() { smol::block_on(async { if let Err(e) = __rsh_session().await { eprintln!("{}", e); } }); }
- Tokio:
- Start in sync mode.
- After each
cargo run --bin __rsh:- If it succeeds, nothing special happens.
- If it fails, and the stderr output looks async-related (e.g. contains
E0728, “only allowed inside async functions”,async fn main, etc.),rsh:- Scans the current project’s
Cargo.toml(as plain text) for async runtimes:- Prefers
tokio, thenasync-std, thensmol.
- Prefers
- If a supported runtime is found:
- Switches the session permanently to async mode with that runtime.
- Regenerates
src/bin/__rsh.rsin async form. - Reruns
cargo run --bin __rshonce and prints its output.
- If no supported runtime is found:
- Prints a clear message asking the user to add
tokio,async-std, orsmolto theirCargo.toml. - Stays in sync mode.
- Prints a clear message asking the user to add
- Scans the current project’s
- Once in async mode,
rshdoes not attempt further automatic switches; it just prints compiler/runtime errors and returns to the prompt.
- Rust compiler and runtime errors from
cargo run --bin __rshare printed verbatim. rshdoes not attempt to fix or reinterpret user code.- The tool itself only exits on:
- User request (
:quit/:q). - Non-recoverable internal errors (e.g. I/O failures,
cargonot found).
- User request (
- Auto-import resolution.
- Feature flag forwarding.
- Workspace support.
- Expression auto-printing.
- Persistent runtime state across sessions.
- LLM-generated code execution.
- User-controlled cleanup flag(cleanup target dir).