https://github.com/cargo-runner/cargo-runner
A sophisticated scope-based runnable detection tool for Rust that supports multiple build systems (Cargo, Bazel, Rustc) and provides intelligent command generation for tests, benchmarks, binaries, and doc tests.
https://github.com/cargo-runner/cargo-runner
clap-rs cli js rust typescript vscode vscode-extension wasm wit-bindgen
Last synced: 2 months ago
JSON representation
A sophisticated scope-based runnable detection tool for Rust that supports multiple build systems (Cargo, Bazel, Rustc) and provides intelligent command generation for tests, benchmarks, binaries, and doc tests.
- Host: GitHub
- URL: https://github.com/cargo-runner/cargo-runner
- Owner: cargo-runner
- License: mit
- Created: 2024-04-08T14:48:37.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-08-15T08:52:49.000Z (10 months ago)
- Last Synced: 2025-08-15T10:13:31.990Z (10 months ago)
- Topics: clap-rs, cli, js, rust, typescript, vscode, vscode-extension, wasm, wit-bindgen
- Language: Rust
- Homepage:
- Size: 1.57 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Cargo Runner
The core build engine for the `cargo-runner` project. Handles command generation, build-system detection, framework dispatch, and per-function override resolution for Cargo, Bazel, Rustc, single-file-script targets, and custom frameworks like Dioxus, Leptos, and Tauri.
---
## Installation
The recommended way to install is via `cargo-binstall` to download pre-compiled binaries directly from GitHub Releases:
```bash
cargo binstall cargo-runner-cli
```
Alternatively, you can build from source:
```bash
cargo install cargo-runner-cli
```
---
## Build System & Framework Detection
`UnifiedRunner` uses a Plugin Registry to allow overrides (framework overlays) before falling back to generic build-system detection:
```
┌─ Framework Overlays (highest priority) ───────────────────────┐
│ Dioxus.toml in ancestor dirs → DioxusOverlayPlugin │
│ "leptos" in Cargo.toml → LeptosOverlayPlugin │
└───────────────────────────────────────────────────────────────┘
│ (no plugin claimed the path)
▼
┌─ Build system detection ──────────────────────────────────────┐
│ MODULE.bazel present → BazelRunner │
│ Cargo.toml present → CargoRunner │
│ (none) → RustcPrimaryPlugin │
└───────────────────────────────────────────────────────────────┘
```
### Framework vs Bazel — design boundary
Framework-managed projects (Dioxus, Leptos, Tauri) **always use their native CLI**, never Bazel. These frameworks orchestrate WASM compilation, asset bundling, hot-reload dev servers, and platform-specific builds internally — capabilities that Bazel cannot replicate.
Bazel support targets **pure Rust projects**: API servers, CLI tools, libraries, and monorepos with shared dependency graphs.
| Framework | CLI | Bazel support? |
|-----------|-----|----------------|
| Dioxus | `dx serve / dx build` | ❌ Not supported — use `dx` |
| Leptos | `cargo leptos watch / build` | ❌ Not supported — use `cargo-leptos` |
| Tauri | `cargo tauri dev / build` | ❌ Not supported — use Tauri CLI |
| Pure Rust (lib, bin, tests) | `cargo` or `bazel` | ✅ Fully supported |
---
## Implicit Execution & Target Inference
When running `cargo runner run` without explicit file arguments, the runner intelligently infers what to execute based on Cargo's `default-run` settings and standard Rust project conventions.
It uses these exact filesystem layout patterns to automatically detect binaries, tests, benchmarks, and libraries—both for resolving implicitly executed targets and for generating underlying Bazel rules:
| Rust Source Path | Inferred Target Kind | Notes / Bazel Mapping |
|------------------|----------------------|-----------------------|
| `src/main.rs` | Project Binary | The default entry point, maps to `rust_binary` |
| `src/lib.rs` | Library & Doc Tests | Maps to `rust_library` and `rust_doc_test` |
| `src/bin/*.rs` | Additional Binary | Scaffolded/detected only if `fn main()` is present |
| `src/bin/*/main.rs` | Directory Binary | Scaffolded/detected only if `fn main()` is present |
| `tests/*.rs` | Integration Test | Automatically maps to `rust_test_suite` |
| `examples/*.rs` | Example Binary | Scaffolded/detected only if `fn main()` is present |
| `benches/*.rs` | Benchmark | Scaffolded/detected only if `fn main()` is present |
| `build.rs` | Build Script | Maps to `cargo_build_script` internally |
**Priority:** Explicit `Cargo.toml` definitions (`[[bin]]`, `[[test]]`, `[[bench]]`, `[[example]]`) always take priority over the filesystem conventions above.
**`fn main()` heuristic**: Files in `src/bin/` and `examples/` are only detected as runnable binaries if they actually contain a `fn main()` function — helper modules are silently skipped.
---
## Scoped Execution
`cargo runner run ` detects the surrounding project context and automatically builds the correct compilation and execution command. To preview any command without executing it, simply append `--dry-run`.
It evaluates the environment in this specific priority:
### 1. Standalone Rust Files (`rustc`)
If a Rust file sits outside of any framework or build system (no `Cargo.toml`, no Bazel), Cargo Runner transparently falls back to `rustc`.
```bash
cargo runner run standalone.rs
# Generates: rustc standalone.rs -o /tmp/... && /tmp/...
```
### 2. Single-file Scripts
Cargo Runner recognizes single-file Rust scripts explicitly via their shebangs and a `fn main()` entry point.
**Cargo Nightly Script Example (`-Zscript`):**
```rust
#!/usr/bin/env -S cargo +nightly -Zscript
---cargo
[package]
edition = "2021"
---
fn main() { println!("nightly cargo script!"); }
```
```bash
cargo runner run my_script.rs
# Generates: cargo +nightly -Zscript my_script.rs
```
**Rust-Script Example:**
```rust
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! anyhow = "1"
//! ```
fn main() { println!("rust-script execution!"); }
```
```bash
cargo runner run my_rust_script.rs
# Generates: rust-script my_rust_script.rs
```
### 3. Cargo Projects & Workspaces
Inside a standard Cargo project or massively scaled virtual Workspace, execution paths are mapped intelligently:
```bash
cargo runner run src/main.rs
# Generates: cargo run --bin my_app
cargo runner run tests/integration.rs
# Generates: cargo test --test integration
```
| What you cursor into | Cargo generates |
|---------------------|-----------------|
| `#[test] fn test_add()` | `cargo test test_add --exact` |
| `mod tests { }` block | `cargo test tests::` |
| `/// ``` doctest` | `cargo test --doc add` |
| `fn main()` binary | `cargo run --bin name` |
| Benchmark function | `cargo bench name` |
### 4. Bazel Projects
In a Bazel workspace, the runner maps standard Rust layout conventions to their exact Bazel targets so you don't have to think about `//:labels`:
```bash
cargo runner run src/main.rs
# Generates: bazel run //:my_app
cargo runner run src/lib.rs:25
# Generates: bazel test //:unit_tests --test_arg="tests::add"
```
| What you cursor into | Bazel generates |
|---------------------|-----------------|
| `#[test] fn test_add()` | `bazel test //:unit_tests --test_arg="test_add"` |
| `mod tests { }` block | `bazel test //:unit_tests --test_arg="tests::"` |
| `/// ``` doctest` | `bazel test //:doc_tests` |
| `fn main()` binary | `bazel run //:name` |
| Benchmark function | `bazel run //:bench_name -c opt` |
### 5. Dioxus and Leptos (Custom Frameworks)
When working inside a frontend framework, `cargo-runner` hands off execution to the native CLI orchestrator required to run the WebAssembly bundling and hot-reloading dev servers.
```bash
cargo runner run src/main.rs
```
- **Dioxus:** Automatically invokes `dx serve` or `dx build`.
- **Leptos:** Automatically invokes `cargo leptos watch` or `cargo leptos build`.
- **Tauri:** Automatically invokes the `cargo tauri` developer environment.
---
## Tooling & Debugging Commands
Beyond executing files, Cargo Runner provides powerful introspection commands. You can pass raw module paths (like `runners::unified_runner::tests`) or standard file paths to these utilities.
### Previewing Commands (`--dry-run`)
If you aren't sure what command Cargo Runner will synthesize for a specific file or framework, use `--dry-run`. It will print the exact internal `Command` structure without spawning it:
```bash
cargo runner run src/main.rs --dry-run
```
### Exploring Targets (`runnables`)
Use `runnables` to list all valid execution targets. You can filter by `--bin`, `--test`, `--bench`, or substring matches:
```bash
# List all targets in a specific file
cargo runner runnables src/lib.rs
# Search the entire workspace for binaries
cargo runner runnables --bin
# Find a specific module block or test
cargo runner runnables runners::unified_runner::tests --exact
```
### Inspecting Context (`context`)
If you need deep JSON introspection for IDE integration (or debugging why a file resolved a certain way), the `context` command reveals exactly how Cargo Runner interpreted the environment:
```bash
cargo runner context src/main.rs --json
```
When the input is not an existing file, `cargo runner` scans the current workspace members, matches the runnable `module_path`, and resolves the owning file automatically.
---
## Bazel — One-Command Workflow
> **Goal**: Use a Bazel-managed Rust workspace as if it were plain Cargo — no manual Bazel bookkeeping.
### `cargo runner init --bazel`
This is the **single entry point** for all Bazel scaffolding. It handles both initial setup and subsequent syncs.
#### First run — scaffolds the workspace
```bash
cargo runner init --bazel
```
Generates:
- `MODULE.bazel` — bzlmod dependency graph via `crate.from_cargo()`
- `.bazelversion` — pins Bazel 7.4.1
- `.bazelrc` — build flags + shared disk/repo caches
- `BUILD.bazel` — targets for each crate (see below)
- `Cargo.lock` — required by `crate_universe`
- `.cargo-runner.json` — framework defaults
Then runs:
1. `bazel sync` — downloads toolchain + resolves crate deps
2. `bazel build --nobuild //...` — validates all BUILD files without compiling
#### Re-run — idempotent sync
```bash
cargo runner init --bazel # safe to re-run anytime
```
Re-scans source files, adds missing targets, skips existing ones. The single command replaces the old `build-sync` workflow.
#### Workspace support
For Cargo workspaces, `init --bazel` automatically:
- Parses `[workspace] members` (supports explicit lists and globs)
- Generates per-member `BUILD.bazel` files
- Creates a unified `MODULE.bazel` at the root
### Doctests
Library crates (`src/lib.rs`) automatically get a `rust_doc_test` target:
```python
rust_doc_test(
name = "doc_tests",
crate = ":my_lib",
)
```
Doctests use Bazel natively. There is **no cargo fallback** — if it's a Bazel project, everything goes through Bazel.
### BUILD.bazel safety model
`build-sync` only modifies lines inside a **managed block** — anything outside the fences is left untouched:
```python
# Hand-authored rules above are NEVER touched
# BEGIN cargo-runner-managed — do not edit this block manually
rust_library(...)
rust_test(...)
rust_doc_test(...)
# END cargo-runner-managed
```
Deduplication is name-aware and content-aware:
- Exact name matches are skipped
- Any existing `rust_doc_test(` rule (regardless of name) prevents duplicate doc test targets
### Other commands
| Command | What it does |
|---------|-------------|
| `cargo runner add [--features f] [--dev]` | `cargo add` + `cargo update` + `bazel sync` + `gen_rust_project` in one shot |
| `cargo runner sync [--crate ] [--skip-ide]` | Sync Bazel crate-universe after any `Cargo.toml` edit |
| `cargo runner build-sync [--crate ] [--dry-run]` | Update `BUILD.bazel` targets (also runs as part of `init --bazel`) |
| `cargo runner clean` | Context-aware clean: `bazel clean` (Bazel) or `cargo clean` (Cargo) |
| `cargo runner watch` | Context-aware file watcher: `ibazel` (Bazel) or `cargo watch` (Cargo) |
| `cargo runner run [:]` | Scope-based execution: detects build system and runs the target at the given line or module path |
| `cargo runner runnables [file\|module::path[:line]] [--bin] [--test] [--bench] [--doc] [--name QUERY] [--symbol SYMBOL] [--exact]` | List runnable items for a file, module path, or entire workspace |
| `cargo runner context [file\|module::path[:line]] --json` | Emit machine-readable project/file context for TMP and other tooling |
---
## Configuration Reference
Configuration lives in `.cargo-runner.json` at your crate root.
### Top-level shape
```json
{
"bazel": { ... },
"cargo": { ... },
"overrides": [ ... ]
}
```
### Bazel project config (`"bazel"`)
Used at the project level to configure how Bazel commands are built.
```json
{
"bazel": {
"workspace": "my_workspace",
"test_framework": {
"command": "bazel",
"subcommand": "test",
"target": "{target}",
"args": ["--test_output", "streamed"],
"test_args": ["--nocapture", "{test_filter}"]
},
"binary_framework": {
"command": "bazel",
"subcommand": "run",
"target": "{target}"
},
"benchmark_framework": {
"command": "bazel",
"subcommand": "test",
"target": "{target}",
"args": ["--test_output", "streamed", "--test_arg", "--bench"],
"test_args": ["{bench_filter}"]
}
}
}
```
Supported template placeholders: `{target}`, `{test_filter}`, `{bench_filter}`, `{file_name}`.
### Per-function overrides (`"overrides"`)
The `overrides` array lets you customize commands for specific functions, tests, or files. Each entry has a `"match"` key and a command-type block.
#### Cargo override
```json
{
"match": {
"function_name": "my_slow_test",
"package": "server"
},
"cargo": {
"extra_args": ["--test-threads=1"],
"extra_env": { "RUST_BACKTRACE": "1", "RUSTFLAGS": "-Awarnings" }
}
}
```
**Note on Cargo Environments**: Overrides correctly inject environment variables into `cargo` commands contextually. Due to accurate `FileType` contextual propagation, `RUSTFLAGS` or other systemic flags apply perfectly onto `cargo run`/`cargo test` invocations without mistakenly triggering standalone `rustc` fallbacks.
#### Bazel override
The `"bazel"` block inside an override is a **flat `BazelOverride`** — fields are promoted from the framework level directly to the override. You no longer need to nest `test_framework.test_args`.
```json
{
"match": {
"function_name": "it_works_too",
"module_path": "tests",
"package": "frontend"
},
"bazel": {
"test_args": ["--nocapture"]
}
}
```
All `BazelOverride` fields:
| Field | Type | Description |
|-------|------|-------------|
| `command` | `string` | Override the Bazel binary (e.g. `"bazelisk"`). Display/tooling only — not yet applied at runtime. |
| `subcommand` | `string` | Override the subcommand (`"test"`, `"run"`, `"build"`). |
| `target` | `string` | Override the target label. |
| `args` | `string[]` | Replace the base args block (after subcommand + target). |
| `extra_args` | `string[]` | Append verbatim args after the base args. |
| `test_args` | `string[]` | Inject as `--test_arg ` pairs. |
| `exec_args` | `string[]` | Append after `--` separator (for `bazel run`). |
| `extra_env` | `object` | Merge extra environment variables. |
> **Migration note**: The previous nested form `"bazel": { "test_framework": { "test_args": [...] } }` inside overrides **is no longer valid**. Update to the flat shape above.
### Override CLI
Use `cargo runner override` to create overrides from the command line instead of editing JSON manually.
#### Named flags
```bash
cargo runner override --command --subcommand --channel
```
| Flag | What it sets |
|------|-------------|
| `--command` | The command binary (`dx`, `cargo`, `bazel`) |
| `--subcommand` | The subcommand (`serve`, `run`, `build`, `watch`) |
| `--channel` | Rust toolchain channel (`nightly`, `stable`) |
#### Token syntax (after `--`)
```bash
cargo runner override --
```
| Token | Effect |
|-------|--------|
| `@cmd.sub` | Set command + subcommand (e.g., `@dx.run`) |
| `+channel` | Set Rust toolchain channel (e.g., `+nightly`) |
| `KEY=value` | Set environment variable (e.g., `RUST_LOG=debug`, `RUSTFLAGS="-Awarnings"`) |
| `/args...` | Test binary args (like `--` in `cargo test`) |
| `-command` | Remove the command override |
| `-env` | Remove all env overrides |
| `-` | Remove the entire override |
| other | Appended as `extra_args` |
> **Environment Variables**: Overrides like `KEY=value` reliably populate the `extra_env` record. For Bazel targets, these are accurately transformed strictly into `--action_env=KEY=value` (and `--test_env=KEY=value` for valid subcommands). For Cargo, these correctly attach to the `Command` execution environment across nested workspace crates.
#### Dioxus examples
```bash
# Default: dx serve — override to dx run
cargo runner override src/main.rs --command dx --subcommand run
# Same using token syntax
cargo runner override src/main.rs -- @dx.run
# Add --release flag
cargo runner override src/main.rs -- @dx.serve --release
# Add env vars
cargo runner override src/main.rs -- @dx.serve RUST_LOG=debug
```
#### Leptos examples
```bash
# Default: cargo leptos serve — override to cargo leptos watch
cargo runner override src/main.rs --subcommand watch
# Same using token syntax (all three forms are equivalent)
cargo runner override src/main.rs -- @cargo.watch
cargo runner override src/main.rs -- @cargo.leptos.watch
# Add --release
cargo runner override src/main.rs --subcommand build -- --release
```
> **Note**: For Leptos, you only set `--subcommand` (not `--command`) because the command is always `cargo` — the runner constructs `cargo leptos ` automatically.
#### Test binary args (ignored tests, etc.)
The `/` token passes arguments directly to the test binary (after `--` in `cargo test`):
```bash
# Run all tests including ignored ones
cargo runner override src/lib.rs -- /--include-ignored
# Run only ignored tests
cargo runner override src/lib.rs -- /--ignored
# Add multiple test binary args
cargo runner override src/lib.rs -- /--include-ignored --nocapture
```
Key distinction:
- `-- extra_args` → cargo-level flags (before `--`)
- `/ test_args` → test binary flags (after `--`)
#### Workspace-wide test binary args
To apply test binary args to **all tests in the workspace**, set `extra_test_binary_args` at the top-level `cargo` config in `.cargo-runner.json`:
```json
{
"cargo": {
"extra_test_binary_args": ["--include-ignored"]
}
}
```
This applies globally without needing per-file overrides.
#### Remove an override
```bash
cargo runner override src/main.rs -- -
```
---
## Waz Integration
If you use `waz`, the same lookup model is available there too:
```bash
waz run src/main.rs:25
waz run runners::unified_runner::tests
waz runnables
waz runnables runners::unified_runner::tests
```
`waz run` is the non-interactive path; it reuses the same project and
module-path resolution so you can skip the TUI when you already know what you
want to run.
`waz runnables` is the companion listing command when you want to inspect the
available run targets first, either for the whole workspace or for a specific
module path.
`--bin`, `--test`, `--bench`, and `--doc` narrow the result set by runnable
kind. `--name` does a case-insensitive, punctuation-insensitive substring
match against the label, module path, and function name. Add `--exact` to
require the normalized name to match exactly instead of by substring, so
`foo bar`, `foo_bar`, and `FooBar` still collapse to the same search key but
`foo` will no longer match `foobar` when `--exact` is present.
`--symbol` filters symbol-like targets, such as doc-tested structs, enums,
unions, module test groups, and binary names. It can be combined with `--name`
and the kind filters.
`cargo runner run` accepts the same selector styles:
- bare function or method name: `cargo runner run test_helper`
- full module path plus function: `cargo runner run runners::unified_runner::tests::test_helper`
- doc-test symbol: `cargo runner run Users`
---
## License
MIT or Apache-2.0, at your option.