rusty-pv
Show progress, elapsed time, ETA, rate, & bytes transferred while data flows through a pipe. Rust port of Andrew Wood's pv(1) 1.10.5.
Drop it into any pipe to watch the bytes move: rusty-pv backup.tar | gzip > backup.tar.gz draws a live progress bar with ETA & transfer rate. It adds a token-bucket throttle (-L), EMA-smoothed rate display, & IEC/SI unit math, plus a typed library API. Default mode adds parse-time conflict rejection, --help/--version, & a completions subcommand. Strict mode reverts every observable surface to byte-equal pv 1.10.5 for drop-in migration.
Part of the Rusty portfolio.
Install
# or, with prebuilt binaries:
# or, download directly from GitHub Releases:
# https://github.com/jsh562/rusty-pv/releases
Usage
# Show progress copying a file to a block device
# Throttle a copy to 5 MiB/s (avoid saturating a slow disk or network link)
# Label a stage in a multi-stage pipeline so you can see which is slow
| |
# Numeric mode for dialog --gauge integration (one percentage per line)
# Cursor mode for multiple pv instances in one pipeline (Unix only)
| | |
# Strict pv-compat mode (drop-in pv 1.10.5 replacement, byte-equal stderr)
RUSTY_PV_STRICT=1
# Shell completions
Library API
The library exposes the Pv / PvBuilder / Reporter / Progress types, the token-bucket throttle, the EMA smoother, & the IEC/SI unit math without any CLI deps. Use it when you want pv-style throttling & reporting inside another Rust tool.
use ;
use Cursor;
let pv = new
.total_bytes
.rate_limit // 500 kB/s
.build;
let mut reader = new;
let mut writer = Vecnew;
let n = pv.copy.unwrap;
assert_eq!;
For library-only consumers without CLI deps see the Cargo Features section.
Cargo Features
default enables full, which (for this single-capability port) resolves to the cli umbrella. pv-classic reproduces v0.1.x bare-port behavior matching upstream pv 1.10.5 1:1. To strip the CLI surface use default-features = false or --no-default-features & add the features you want.
rusty-pv is a single-capability port: its one documented job is "show pv-style progress / throughput / ETA / rate while data flows through a pipe". No optional feature leaves are carved beyond the required umbrellas; see docs/feature-layout.md for why.
Feature matrix
| Feature | Description | Umbrella(s) |
|---|---|---|
cli |
All CLI-only dependencies (clap, clap_complete, anyhow, crossterm, signal-hook on Unix, fd-lock) and the binary entry point, mode resolver, stderr renderer, cursor coordinator, signal handlers, and Strict-mode pre-scanner. Library consumers strip via default-features = false. |
full, pv-classic, pv-minimal |
Preset bundles
| Bundle | Composition | Use case |
|---|---|---|
pv-classic |
cli |
Drop-in upstream pv 1.10.5 replacement. Strict mode is invoked via --strict, RUSTY_PV_STRICT, or pv/pv-alias argv[0] auto-detect. |
pv-minimal |
cli |
Explicit minimal-CLI alias for users who prefer the <port>-minimal naming convention seen across other portfolio ports. Identical composition to pv-classic. |
Keep-list workaround (Cargo features are union-only)
Cargo features cannot subtract from default. To get "everything except a specific feature," disable defaults & enumerate the features you want:
# → bare CLI. Equivalent to pv-classic / pv-minimal.
For the common cases the named preset bundles are usually sufficient.
Library-only consumers
[]
= { = "0.2", = false }
This strips clap, clap_complete, anyhow, crossterm, signal-hook, & fd-lock. The resulting build pulls only thiserror (required by the always-on PvError enum).
Convention authority
This layout follows the portfolio-wide Cargo Features Convention. The "why" lives in ADR-0006; the "what" lives in project-instructions.md §Cargo Feature Surface. Every Rusty port from v0.2 onward exposes the same umbrella set (default / full / cli / <port>-classic), per-port leaves named in kebab-case, & 2 to 4 preset bundles.
Compatibility
rusty-pv has two modes:
- Default mode. clap-styled flag parser. Conflicting flag pairs MUST be rejected at parse time.
--help,--version, & thecompletionssubcommand are all available. - Strict mode (activated by
--strict,RUSTY_PV_STRICT=1, or invoking the binary aspv/pv-alias). Byte-equal stderr against upstream v1.10.5 for documented diagnostics. Last-wins flag resolution.--help,--version, &completionsMUST be rejected.
The EMA smoothing constant α = 0.3 is locked at v0.1.0 to preserve byte-equal Strict-mode compatibility. Future versions may expose it via an additive library method.
Display modes
- Single-line redraw (default). Carriage-return + erase-to-EOL ANSI sequence. Works on every terminal that supports basic ANSI.
- Cursor mode (
-c). Unix-only in v0.1.0. Coordinates multiplerusty-pvinstances in a pipeline so each writes its own row. Windows emits a stderr diagnostic & falls back to single-line redraw.
Concurrency
The -c per-tty file lock guarantees only one rusty-pv instance writes ANSI cursor escapes at a time. The cursor-mode design isn't crash-safe; if a rusty-pv instance is killed mid-redraw the next surviving instance may briefly overwrite its row. This matches upstream pv behavior.
See docs/COMPATIBILITY.md for the full per-flag matrix.
What's not shipped
-Fcustom format strings. Default format is fine for the 99% case. Forward-review candidate.-Tbuffer percentage display. Internal-buffer-fill UI; not part of the byte-equal Strict contract.-Apeek window. Niche; deferred.--bitsbits-per-second display. Trivially added later as an additive flag.--remote/--queryinter-instance control. Multi-process IPC; out of scope for v0.1.0.- SIGUSR2 handling. Deprecated by upstream pv.
- Cursor mode on Windows.
-cfalls back to single-line redraw with a stderr diagnostic.
MSRV
Rust 1.85 (edition 2024). Re-verified against the portfolio's stable-minus-two policy at each release.
License
Dual-licensed under MIT or Apache-2.0 at your option.