rusty-pv 0.2.0

Pipe viewer — a Rust port of Andrew Wood's `pv(1)` with progress bar, ETA, rate display, token-bucket rate limiting, IEC/SI unit math, SIGWINCH-aware terminal redraw, SIGUSR1 size refresh, multi-instance cursor coordination, and a typed library API.
Documentation

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.

crates.io docs.rs CI MSRV license: MIT OR Apache-2.0

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

cargo install rusty-pv
# or, with prebuilt binaries:
cargo binstall rusty-pv
# or, download directly from GitHub Releases:
# https://github.com/jsh562/rusty-pv/releases

Usage

# Show progress copying a file to a block device
rusty-pv ubuntu.iso > /dev/sdb

# Throttle a copy to 5 MiB/s (avoid saturating a slow disk or network link)
rusty-pv -L 5M big.iso > out.iso

# Label a stage in a multi-stage pipeline so you can see which is slow
some-cmd | rusty-pv -N "stage 1" | another-cmd

# Numeric mode for dialog --gauge integration (one percentage per line)
rusty-pv -n -s 100M big.iso > out.iso

# Cursor mode for multiple pv instances in one pipeline (Unix only)
producer | rusty-pv -c -N gen | gzip | rusty-pv -c -N gz > out.gz

# Strict pv-compat mode (drop-in pv 1.10.5 replacement, byte-equal stderr)
rusty-pv --strict ubuntu.iso > /dev/sdb
RUSTY_PV_STRICT=1 rusty-pv ubuntu.iso > /dev/sdb
pv ubuntu.iso > /dev/sdb                   # via pv-alias argv[0] symlink

# Shell completions
rusty-pv completions bash                   # > ~/.bash_completion.d/rusty-pv
rusty-pv completions zsh                    # > ~/.zfunc/_rusty-pv
rusty-pv completions fish                   # > ~/.config/fish/completions/rusty-pv.fish
rusty-pv completions powershell

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 rusty_pv::{Pv, PvBuilder};
use std::io::Cursor;

let pv = PvBuilder::new()
    .total_bytes(1024 * 1024)
    .rate_limit(500_000) // 500 kB/s
    .build();
let mut reader = Cursor::new(vec![0u8; 1024 * 1024]);
let mut writer = Vec::new();
let n = pv.copy(&mut reader, &mut writer).unwrap();
assert_eq!(n, 1024 * 1024);

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:

cargo install rusty-pv --no-default-features --features "cli"
# → bare CLI. Equivalent to pv-classic / pv-minimal.

For the common cases the named preset bundles are usually sufficient.

Library-only consumers

[dependencies]
rusty-pv = { version = "0.2", default-features = 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, & the completions subcommand are all available.
  • Strict mode (activated by --strict, RUSTY_PV_STRICT=1, or invoking the binary as pv/pv-alias). Byte-equal stderr against upstream v1.10.5 for documented diagnostics. Last-wins flag resolution. --help, --version, & completions MUST 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 multiple rusty-pv instances 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

  • -F custom format strings. Default format is fine for the 99% case. Forward-review candidate.
  • -T buffer percentage display. Internal-buffer-fill UI; not part of the byte-equal Strict contract.
  • -A peek window. Niche; deferred.
  • --bits bits-per-second display. Trivially added later as an additive flag.
  • --remote / --query inter-instance control. Multi-process IPC; out of scope for v0.1.0.
  • SIGUSR2 handling. Deprecated by upstream pv.
  • Cursor mode on Windows. -c falls 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.