rusty-vipe
A Rust port of the moreutils vipe utility: pop $EDITOR mid-pipe so you can edit the buffered bytes interactively, then resume the pipeline with the edited output. Static binaries on Linux, macOS, and Windows; works with or without a Rust toolchain via cargo install or cargo binstall. Default mode adds quality-of-life niceties moreutils doesn't have (--help, --version, --editor=<cmd>, completions); Strict mode mirrors moreutils' error layout for drop-in migration.
Part of the Rusty portfolio — a collection of small Rust ports of utilities missing from the Rust ecosystem.
Install
With a Rust toolchain
To also install the vipe binary alias (auto-enables Strict mode on invocation):
Without a Rust toolchain (prebuilt binaries via cargo-binstall)
Direct download
Per-target archives are attached to each GitHub Release. Linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64. Each archive contains the binary plus pre-generated shell-completion scripts for bash, zsh, fish, and PowerShell.
Usage
# Edit-in-pipe: pops $EDITOR on the buffered stdin, resumes with edited bytes.
| |
# Suffix hint for syntax highlighting (default .txt).
| |
# Explicit editor override (Default mode only).
| |
# Strict moreutils-compat mode.
| |
RUSTY_VIPE_STRICT=1 | |
| |
# Shell completions
Compatibility statement (vs moreutils vipe)
Byte-level fidelity for documented Strict-mode inputs is verified by snapshot tests against captured moreutils-vipe behavior; behavioral assertions use a deterministic fake-editor helper (committed under tests/bin/) rather than driving real interactive editors in CI. Pinned upstream version: moreutils 0.69 (same baseline as rusty-sponge).
Editor resolution order: --editor=<cmd> (Default mode only) > $VISUAL > $EDITOR > /usr/bin/editor (Unix only; executable check) > vi (Unix) / notepad.exe (Windows).
Pipeline-safety guarantee: When the editor exits non-zero, rusty-vipe does NOT forward the tempfile bytes to the downstream consumer and exits non-zero (preserving the editor's exit code where possible — clamped to 1 on Windows for codes > 254). This is the safety contract that lets users abort dangerous pipelines via the editor's exit-with-error command (e.g., :cq in vim).
Documented intentional divergences from moreutils vipe:
--help/--version/--editor=flags +completionssubcommand: not present in moreutils. Default-mode additions; rejected in Strict mode.- Unknown-flag stderr format in Strict mode: emits ONLY the first unknown-flag error (
rusty-vipe: invalid option -- 'X'orrusty-vipe: unknown option -- 'foo'). moreutils' POSIXGetopt::Longiterates per-character for malformed long flags; we don't replicate that for undocumented inputs (same posture asrusty-spongeSTF-003 option A). - Windows editor fallback:
notepad.exe(always present). moreutils Perl source assumes Unixvi. - No controlling TTY: rusty-vipe emits a precise error and exits non-zero rather than scrambling the pipeline by reading from the producer's pipe stdin.
- Exit-code clamping on Windows (AD-012): editor exits 1–254 pass through verbatim; exit codes greater than 254 or negative collapse to 1. Unix passes 1–255 verbatim. moreutils on Windows is not well-defined here.
shell-wordsargv splitting (HINT-004):EDITORand--editor=are split using POSIX shell-quoting rules via theshell-wordscrate, not raw whitespace. Quoted paths (e.g.EDITOR='"/Program Files/Code/bin/code" --wait') work correctly with rusty-vipe; with moreutils they break.- Windows console-host support:
cmd.exeand PowerShell are first-class;mintty(Git Bash, MSYS2) is a documented divergence — wrap withwinpty rusty-vipe …if needed.
vipe-alias PATH-collision warning. Building with --features vipe-alias installs a second binary named vipe alongside rusty-vipe. If moreutils is also installed on the same PATH, whichever directory comes first wins. Invoke rusty-vipe (always unambiguous) or omit the vipe-alias feature when moreutils is also present.
See docs/COMPATIBILITY.md for the full per-flag matrix.
Library API
The crate exposes a public Rust API for programmatic use. The canonical surface takes generic Read for input and generic Write for output, so library consumers can drive the pipeline against in-memory buffers (in tests, in TUI apps, etc.) without hijacking process stdio.
use ;
use Cursor;
let mut input = new;
let mut output: = Vecnew;
let mut vipe = new
.editor
.suffix
.compat
.build?;
vipe.run?;
# Ok::
To use the library without pulling in the CLI dependencies:
[]
= { = "0.1", = false }
Stability commitment
Lockstep SemVer: the library and binary share a single crate version. Within the 0.x series, minor version bumps may introduce breaking changes per standard Cargo semantics — pin to the patch version (= "0.1.0") if breakage is a concern. Once 1.0 lands, the API is frozen to additive-only changes guarded by #[non_exhaustive] on every public enum and struct.
MSRV
Minimum supported Rust version: 1.85.
Upward deviation from the Rusty portfolio's "current stable minus two" rule, forced by Rust edition 2024 (which requires 1.85+).
Relationship to moreutils
rusty-vipe is a clean-room Rust reimplementation of the moreutils vipe utility. It contains no source code from moreutils — only a from-scratch Rust implementation that observes the documented behavior and reproduces it.
The moreutils vipe Perl source is © Joey Hess and the moreutils contributors, licensed under the GNU GPL. That license governs the Perl source code. Behavioral interfaces (flag set, exit codes, editor invocation order) are not copyrightable, so a clean-room reimplementation under a different license is well-established practice — the same posture as uutils/coreutils.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.