feat(derive): let parse() answer a failure the way a program does - #910
Conversation
The second thing porting communique turned up. `parse()` rendered a failure with `{:?}` —
`UnknownFlag { token: [45, 45, 110, 111, 112, 101] }` — while the clap-shaped rendering
sat in this crate unused. So communique's `main.rs` hand-rolled thirty-four lines to
reach it, which is not a thing an adopter should have to work out.
`parse()` is the entry point that *is* the process: it already printed a help page and
exited. It now prints the message to stderr and exits 2, which is clap's status, so a
script that checks for it keeps working. `parse_from` still hands the error back, for a
library embedding a CLI that wants to decide.
The renderer is reached through `render_failure` in usage-argv rather than by generating
a `#[cfg]`. That is not a stylistic choice: whether the good rendering exists is a
feature of *usage-argv in the adopter's graph*, and a `cfg` written into generated code
is evaluated in the adopter's crate, where the feature is not theirs to see — which is
exactly how a metadata field once got silently dropped. Without `diagnostics` the same
function gives the Debug form, which is what a parser-only build asked for.
With this and the commit below it, communique's port matches clap on `--version`, `-V`,
every error message tested, and every exit code — and `main.rs` is thirty-four lines
shorter than the port needed yesterday.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThe PR changes generated
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (2): Last reviewed commit: "test(argv): read the message, not the es..." | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 91c27e4. Configure here.
Instruction counts
No instruction-count regression above 1%. Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run. Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes. Shadow comparisonParsing
|
`render_failure` styles for the terminal it finds itself in, so under `CLICOLOR_FORCE=1`
— or any TTY — the assertion read
\x1b[1m\x1b[31merror:\x1b[0m unexpected argument '\x1b[33m--nope\x1b[0m' found
and failed on a message that was perfectly correct. A test whose result depends on the
ambient terminal is a flake waiting for the machine that has one.
Stripped before reading. What this test is about is the wording; the colouring has tests
of its own. Checked green under a plain run, `CLICOLOR_FORCE=1` and `NO_COLOR=1`.
Found by Cursor Bugbot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Real, and a good catch — fixed in the head commit. Reproduced before fixing: a message that is perfectly correct, failing a The message is stripped before it is read. What the test is about is the wording; the colouring has tests of its own. Green under a plain run, AI-assisted — Tool: Claude Code; model: anthropic/claude-opus-5; version: unavailable. |

The second regression from the communique port.
parse()rendered a failure with{:?}:while the clap-shaped rendering sat in this crate unused. communique's
main.rshand-rolled 34 lines to reach it, which is not something an adopter should have to work out.parse()is the entry point that is the process — it already printed a help page and exited. It now prints the message to stderr and exits 2, clap's status, so a script checking for it keeps working.parse_fromstill hands the error back, for a library embedding a CLI that wants to decide.Why the renderer is reached through a function rather than a
#[cfg]Not stylistic. Whether the good rendering exists is a feature of usage-argv in the adopter's graph, and a
cfgwritten into generated code is evaluated in the adopter's crate, where the feature is not theirs to see — which is exactly how a metadata field got silently dropped earlier in this project. So the cfg lives beside the thing it gates:render_failureis the clap-shaped message withdiagnosticson, and the Debug form without it, which is what a parser-only build asked for.What it buys the adopter
With this and #909, communique's port matches clap on
--version,-V, every error message tested, and every exit code:and
main.rsis 34 lines shorter than the port needed before it.Breaking:
parse()returnsSelfrather thanResult<Self, String>. Nothing is released yet.Verification
Mutation: making
render_failurefall back to the Debug form withdiagnosticson fails the new test. Both feature configurations —specalone andspec,diagnostics— build clean.AI-assisted — Tool: Claude Code; model: anthropic/claude-opus-5; version: unavailable.
Note
High Risk
Breaking change to generated
parse()’s return type and all failure/help/version exit paths; every derive adopter’smainmust migrate fromResulthandling to the new process semantics.Overview
Cli::parse()is now a process-style entry point: it returnsSelf(breaking change fromResult<Self, String>), prints help/version to stdout and exits 0, and on real parse failures prints a user-facing message to stderr and exits 2 (clap’s code).parse_fromstill returns errors for embedders that want control.Adds
usage_argv::render_failurein the argv crate with#[cfg]beside the gated renderer: withdiagnostics, output matches clap-shaped diagnostics; without it, a minimalDebug-style line—so generated code does not need feature cfgs in the adopter crate.Conformance adds
strip_ansiand a test thatrender_failurewording matches clap for an unknown flag (e.g.--nope).Reviewed by Cursor Bugbot for commit 8a3201e. Bugbot is set up for automated code reviews on this repo. Configure here.