This Rust 2021 command-line recipe reads a saved text file, or standard input when given `-`, and groups nonblank lines by their last non-whitespace Unicode scalar value. It prints the total number of nonblank lines and the count for each ending character in sorted order. Input is treated only as read-only text.
Before you start
- Intended runner: Rust 1.90.0, edition 2021; this is not a claim about the minimum supported Rust version.
- Standard library only; no external crates, unsafe code, include macros, environment-variable access, threads, networking, subprocesses, or filesystem writes.
- Accept exactly one CLI argument: a UTF-8 file path or `-` for standard input.
- Treat all input as untrusted, read-only UTF-8 text; propagate I/O and decoding failures instead of silently truncating input.
- Count a line as nonblank when its Unicode-whitespace-trimmed content is nonempty; use its last remaining Unicode scalar value as the grouping key.
- Write counts and character groups to stdout, and actionable errors to stderr. Exit nonzero only for actionable failures.
Build and check
- Create `recipe.rs` and place the complete Rust program from the example code in it.
- Compile using `rustc --edition=2021 recipe.rs -o recipe`.
- Run `./recipe -` and enter the example text followed by end-of-file, or save equivalent UTF-8 text and run `./recipe path/to/file.txt`.
- Check that grouping ignores surrounding whitespace, preserves character case, and prints character groups in sorted order.
- Try a nonexistent path and confirm that the program prints a useful error to stderr and exits nonzero.
Limits to understand
- Counts are held in `usize`; the program is intended for ordinary saved-text inspection, not guaranteed to handle inputs exceeding available memory or runtime limits.
- `BufRead::lines` removes line terminators and reports invalid UTF-8 or read errors; a failure stops processing rather than producing partial success output.
- Each line is grouped by its final non-whitespace Unicode scalar value, not by grapheme cluster; a visible character composed of multiple scalar values may therefore be grouped by its final scalar.
- A line containing only Unicode whitespace is excluded.
- Character labels use Rust char::escape_default: non-ASCII scalar values are printed as hexadecimal Unicode escapes such as \u{f1}; ordering follows scalar values.
- The recorded five-case execution did not test malformed UTF-8 bytes or multiple CLI arguments. Those checks are additional manual instructions, not executed coverage claims.
What this example adds
This task is distinct from the existing first-character grouping recipe: it groups by the final non-whitespace character instead. It also differs from line-length and word-length histograms, which aggregate numeric lengths rather than characters. No upstream implementation was supplied; the design uses the standard library’s buffered line reading and an ordered map for deterministic output.
Run it
Compile with `rustc --edition=2021 recipe.rs -o recipe`. Run `./recipe <UTF-8-file>` to read a file or `./recipe -` to read standard input. Provide exactly one argument. The program does not interpret input as instructions or write to files.Example input
Input supplied on standard input using `./recipe -`:
Apple
pear!
tea
Expected output
nonblank_lines: 3
!: 1
a: 1
e: 1

