The report stream was a boxed writer so that it could be a BufWriter
off a terminal and the bare, line buffered stdout on one. A single
BufWriter does for both once the loop flushes it after each operand on
a terminal, which needs no trait object.
The flush is what keeps a terminal reading the way C util-linux's line
buffered stdout does, each -v line in order with the diagnostics around
it. Without it every diagnostic would print ahead of the lines that came
before it, and the report would only appear at exit.
Nothing in the test suite drives a terminal, so that order is not
pinned by a test. I compared it by hand under script(1) against
util-linux 2.42.2: a run mixing renames with failures, the -i prompt,
the -n -o skip report and -i at end of input are all identical.
Off a terminal nothing changes.
Two remain, for inputs the integration suite cannot supply portably: an
empty name, which only a link with an empty target provides and only
macOS lets exist, and a name made only of separators, which means
pointing rename at `/`. The comment on the first said the CLI could not
reach it at all; it says when it can.
The ungated test ran the same argv as
unix::test_every_failing_operand_is_reported_once_and_in_order and
asserted a subset of what it does. It was kept for the Windows leg, but
the one thing it added there, that an operand after a failure is still
renamed, is already pinned on every platform by the tally table: its
`s9 s1` row can only exit 2 if the rename after the failure happened.
No other utility in the tree adjusts argv before clap parses it, and
keeping rename on clap's behavior like the rest is worth more than
matching getopt in this one corner.
Without POSIXLY_CORRECT the rewrite handed argv back untouched, so only
runs with the variable set change. There, an argument after the first
operand that begins with `-` is now read as an option, as it already is
without the variable, where C util-linux reads it as a filename or as
the replacement. `--` still ends the options in both.
Sixty-four tests: the exit-status tally, the three substitution modes,
the path-scope rule, both overwrite guards, symlink mode, byte-oriented
names, and the test_invalid_arg smoke test every other util carries.
They defend the decisions a refactor would plausibly undo rather than
restating what clap already guarantees. The two overwrite predicates and
their opposite treatment of a dangling destination, the short circuit
that runs before any syscall, and the order of stripping against the
existence check each have a test whose only job is to fail if someone
tidies them away.
Diagnostics are asserted only as far as we write them. The errno text
after our half belongs to libc, so the suite does not pin strerror
strings on platforms it cannot run.
The substitution engine keeps its unit tests beside it in subst.rs.
`cargo test` from the workspace root does not run them, because the root
package is the only default member; `cargo test -p uu_rename` does.
rename replaces a substring in filenames. The option surface follows
rename(1): -v, -s, -n, -a, -l, -o and -i, with all three operands
required. Behavior throughout is what util-linux 2.42.2 and 2.40.4 were
both observed doing.
The parts a reader should not have to reverse engineer:
- The exit status is two counters rather than bit flags. 0 means
something was renamed, 1 that everything failed, 2 a mix, and 4 that
nothing matched. An operand that matched nothing, that -o skipped, or
that was declined at an -i prompt counts as neither, so it can neither
degrade a success nor promote a failure.
- Only the final path component is rewritten, unless either operand
holds a separator, which widens the scope to the whole path. Trailing
separators are stripped from the operand, but the existence check
still sees the operand exactly as it was typed.
- An empty substring inserts at every code-unit boundary rather than
matching nowhere, and a substring equal to its replacement exits 4
before any syscall runs.
- The two overwrite guards ask different questions deliberately. The
default path uses a check that follows symlinks, so a dangling
destination is clobbered; -s lstats the new target name, so a dangling
entry blocks it.
- Filenames are carried as OsStr and the substitution engine is generic
over the code unit: bytes on unix, UTF-16 units on Windows. A name
that is not valid Unicode survives the report and the diagnostics
without being replaced.
Known divergences, each structural to clap and Rust rather than a defect
in the port: the --help and --version layout, ANSI escapes on a
terminal, the eager mutual-exclusion check, the rpmatch answer set, and
SIGPIPE, which Rust ignores and the reference dies of.