feat(cli-macros): add fgumi-cli-macros with the multi_options attribute macro - #674
Conversation
|
Important Review skippedNo new commits to review since the last review. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedUse the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe PR adds the Changesmulti_options macro
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant multi_options
participant MultiName
participant clap
multi_options->>MultiName: Generate prefixed fields and conversions
MultiName->>clap: Provide generated argument metadata
clap->>MultiName: Parse command-line values
MultiName->>multi_options: Validate and convert values
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main-runall #674 +/- ##
==============================================
Coverage ? 93.94%
==============================================
Files ? 181
Lines ? 109028
Branches ? 0
==============================================
Hits ? 102423
Misses ? 6605
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/fgumi-cli-macros/src/lib.rs`:
- Around line 131-141: Update parse_annotated_struct to reject structs with
non-empty input.generics before returning the parsed struct, using
syn::Error::new_spanned on the generics and a message identifying multi_options
and its lack of generic-struct support. Preserve the existing named-struct
validation and attribute rejection behavior.
- Around line 686-713: Update check_required_is_enforceable so the Option<T> and
Vec<T> acceptance path also requires !self.has_default, rejecting
default_value/default_values when required is set. Add regression cases covering
defaults on Option<T> and Vec<T> to verify required remains enforceable.
In `@crates/fgumi-cli-macros/tests/behavior.rs`:
- Around line 295-345: Strengthen the un-prefixed flag rejection tests by
asserting clap::error::ErrorKind::UnknownArgument rather than only parse
failure: update the three assertions in
crates/fgumi-cli-macros/tests/behavior.rs lines 295-345 within
unprefixed_alias_does_not_leak_onto_the_parent_command,
short_flag_is_not_propagated, and no_long_alias_spelling_leaks_unprefixed, and
the -m and -T assertions in crates/fgumi-cli-macros/tests/real_world.rs lines
228-234 within sort_options_short_flags_are_not_propagated, using expect_err and
checking the returned error kind.
In `@crates/fgumi-cli-macros/tests/compile_fail.rs`:
- Around line 1-10: Update the module documentation and the test function
documented_panics_fail_the_build in the trybuild coverage to use compile-error
or compile-time diagnostic terminology instead of panic terminology. Keep the
existing compile_fail invocation and test behavior unchanged.
In `@crates/fgumi-cli-macros/tests/real_world.rs`:
- Around line 259-263: Strengthen the assertion in
skip_slot_is_not_exposed_but_survives_the_round_trip by checking for the exact
registered flag name, --sort::order, rather than the bare substring order.
Preserve the test’s intent of verifying that the skip slot is not exposed as a
command-line argument.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7e06a0e6-2ce0-4941-bd05-de53ee7573e4
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock,!**/*.lock
📒 Files selected for processing (32)
.github/workflows/publish.ymlCargo.tomlcrates/fgumi-cli-macros/Cargo.tomlcrates/fgumi-cli-macros/src/lib.rscrates/fgumi-cli-macros/tests/behavior.rscrates/fgumi-cli-macros/tests/compile_fail.rscrates/fgumi-cli-macros/tests/real_world.rscrates/fgumi-cli-macros/tests/smoke.rscrates/fgumi-cli-macros/tests/ui/bad_prefix.rscrates/fgumi-cli-macros/tests/ui/bad_prefix.stderrcrates/fgumi-cli-macros/tests/ui/call_form_long.rscrates/fgumi-cli-macros/tests/ui/call_form_long.stderrcrates/fgumi-cli-macros/tests/ui/command_flatten.rscrates/fgumi-cli-macros/tests/ui/command_flatten.stderrcrates/fgumi-cli-macros/tests/ui/conditional_clap_attr.rscrates/fgumi-cli-macros/tests/ui/conditional_clap_attr.stderrcrates/fgumi-cli-macros/tests/ui/cross_reference_arg.rscrates/fgumi-cli-macros/tests/ui/cross_reference_arg.stderrcrates/fgumi-cli-macros/tests/ui/legacy_clap_attr.rscrates/fgumi-cli-macros/tests/ui/legacy_clap_attr.stderrcrates/fgumi-cli-macros/tests/ui/non_struct.rscrates/fgumi-cli-macros/tests/ui/non_struct.stderrcrates/fgumi-cli-macros/tests/ui/positional_field.rscrates/fgumi-cli-macros/tests/ui/positional_field.stderrcrates/fgumi-cli-macros/tests/ui/private_visibility.rscrates/fgumi-cli-macros/tests/ui/private_visibility.stderrcrates/fgumi-cli-macros/tests/ui/struct_level_command.rscrates/fgumi-cli-macros/tests/ui/struct_level_command.stderrcrates/fgumi-cli-macros/tests/ui/tuple_struct.rscrates/fgumi-cli-macros/tests/ui/tuple_struct.stderrcrates/fgumi-cli-macros/tests/ui/unenforceable_required.rscrates/fgumi-cli-macros/tests/ui/unenforceable_required.stderr
|
@coderabbitai pause |
✅ Action performedReviews paused. |
3c2894c to
0761b94
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
0761b94 to
ade5592
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/publish.yml:
- Around line 93-95: Move the early no-op check in the publish workflow until
after the CRATES list is defined, then evaluate the target-version presence for
every crate in CRATES rather than only the root fgumi crate. Exit only when all
publishable crates already have the target version; otherwise continue
publishing the missing crates.
In `@crates/fgumi-cli-macros/tests/behavior.rs`:
- Around line 511-520: Strengthen the assertion in
required_option_field_is_enforced_by_validate by checking that the formatted
validation error contains the complete missing flag token “--req::needed” as a
distinct/full token, not merely as a substring that also matches
“--req::needed-many”. Keep the existing validation scenario and error-message
context unchanged.
- Around line 281-301: Update short_help_shows_only_the_first_doc_paragraph and
long_help_shows_every_doc_paragraph to inspect the relevant argument’s
get_help() and get_long_help() values rather than searching rendered command
output. Assert the short accessor contains only the summary and excludes the
longer explanation, while the long accessor contains both paragraphs, avoiding
width-dependent render_help behavior.
- Around line 180-195: Strengthen the test
the_companions_own_docs_never_become_the_parents_description to assert that
UndocumentedParent::command().get_about() and get_long_about() are both None,
rather than checking their strings for the current companion-doc wording. Remove
the substring-based loop and retain the contract that an undocumented parent has
no generated descriptions.
In `@crates/fgumi-cli-macros/tests/real_world.rs`:
- Around line 438-448: Replace layout-dependent rendered-help assertions with
exact clap argument accessors: in
crates/fgumi-cli-macros/tests/real_world.rs:438-448, update
each_stage_gets_its_own_help_heading_and_the_parent_keeps_its_own to assert
get_help_heading() for sort::max-memory, group::strategy, and threads; in
crates/fgumi-cli-macros/tests/behavior.rs:281-301, update
short_help_shows_only_the_first_doc_paragraph and
long_help_shows_every_doc_paragraph to assert docs::documented via
Arg::get_help() and Arg::get_long_help(), respectively.
- Around line 80-120: Update the parity tests around SortOptions and the
corresponding group fixture to exercise the production CLI structs directly: use
Sort from src/lib/commands/sort.rs and GroupReadsByUmi from
src/lib/commands/group.rs rather than duplicate test-only option structs. Align
all fixture defaults, fields, and flags with those production definitions, or
move the tests into the owning crate; ensure future production option changes
cause these parity tests to fail.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a082dcf8-f9cb-4ae6-b85a-3217a0768f06
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock,!**/*.lock
📒 Files selected for processing (36)
.github/workflows/publish.ymlCargo.tomlcrates/fgumi-cli-macros/Cargo.tomlcrates/fgumi-cli-macros/src/lib.rscrates/fgumi-cli-macros/tests/behavior.rscrates/fgumi-cli-macros/tests/compile_fail.rscrates/fgumi-cli-macros/tests/real_world.rscrates/fgumi-cli-macros/tests/smoke.rscrates/fgumi-cli-macros/tests/ui/bad_prefix.rscrates/fgumi-cli-macros/tests/ui/bad_prefix.stderrcrates/fgumi-cli-macros/tests/ui/call_form_long.rscrates/fgumi-cli-macros/tests/ui/call_form_long.stderrcrates/fgumi-cli-macros/tests/ui/command_flatten.rscrates/fgumi-cli-macros/tests/ui/command_flatten.stderrcrates/fgumi-cli-macros/tests/ui/conditional_clap_attr.rscrates/fgumi-cli-macros/tests/ui/conditional_clap_attr.stderrcrates/fgumi-cli-macros/tests/ui/cross_reference_arg.rscrates/fgumi-cli-macros/tests/ui/cross_reference_arg.stderrcrates/fgumi-cli-macros/tests/ui/generic_struct.rscrates/fgumi-cli-macros/tests/ui/generic_struct.stderrcrates/fgumi-cli-macros/tests/ui/legacy_clap_attr.rscrates/fgumi-cli-macros/tests/ui/legacy_clap_attr.stderrcrates/fgumi-cli-macros/tests/ui/non_struct.rscrates/fgumi-cli-macros/tests/ui/non_struct.stderrcrates/fgumi-cli-macros/tests/ui/positional_field.rscrates/fgumi-cli-macros/tests/ui/positional_field.stderrcrates/fgumi-cli-macros/tests/ui/private_visibility.rscrates/fgumi-cli-macros/tests/ui/private_visibility.stderrcrates/fgumi-cli-macros/tests/ui/struct_level_command.rscrates/fgumi-cli-macros/tests/ui/struct_level_command.stderrcrates/fgumi-cli-macros/tests/ui/tuple_struct.rscrates/fgumi-cli-macros/tests/ui/tuple_struct.stderrcrates/fgumi-cli-macros/tests/ui/unenforceable_required.rscrates/fgumi-cli-macros/tests/ui/unenforceable_required.stderrcrates/fgumi-cli-macros/tests/ui/where_only.rscrates/fgumi-cli-macros/tests/ui/where_only.stderr
…te macro
Adds the proc-macro crate runall uses to re-expose every per-stage option of
`fgumi sort` / `group` / `simplex` / `duplex` / `codec` without hand-maintaining
a parallel option set. `#[multi_options("sort", "Sort Options")]` takes a
`clap::Args` options struct and generates a `MultiSortOptions` companion whose
flags are named `--sort::<flag>`, plus `validate()`, `TryFrom` and `From`
conversions.
The companion is faithful to the standalone command by construction:
* every `default_value*` attribute is copied verbatim, so the two flags cannot
advertise different defaults and the field type needs no `Display` impl;
* long aliases are re-prefixed and short flags dropped, so nothing
un-namespaced reaches the parent command;
* each generated arg carries its own `help_heading`, so a parent argument
declared after a flattened companion keeps its own heading;
* `#[doc]` attributes are forwarded individually, preserving clap's short/long
help split;
* `#[cfg]` gates are forwarded onto the field and both conversion arms;
* `#[arg(skip)]` fields ride along as skip fields, making `From` + `validate()`
a lossless round trip;
* `Vec<T>` passes through untouched -- clap never consults a struct's `Default`,
so backfilling from it would diverge from the standalone command.
Required-ness stays staged: `required` is dropped from the clap side and
enforced by `validate()`, so a missing value is reported as
`--sort::max-memory is required when sort is selected` rather than by clap's
parser, which cannot name the stage.
Forms the macro cannot re-expose faithfully fail the build with a spanned
`syn::Error` pointing at the offending field: cross-field reference keys whose
arg ids would dangle after prefixing, `id`/`name` overrides, clap's `key(value)`
call form for classified keys, field- and struct-level `#[command(...)]`,
`#[group(...)]`, an unenforceable `required`, a non-identifier prefix, and the
legacy `#[clap(...)]` / `#[structopt(...)]` spellings that every classifier
would otherwise silently ignore.
Tests include parity fixtures carrying the `#[arg(...)]` attributes of the real
`SortOptions` and `GroupOptions` verbatim, asserting the standalone and
prefixed commands parse to identical structs, plus ten trybuild cases pinning
the diagnostics.
Also adds fgumi-cli-macros, fgumi-fmt and fgumi-cli-common to publish.yml's
CRATES list. The latter two are publishable but were never listed, which fails
that workflow's own completeness check on push to main.
ade5592 to
aca8c9a
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
✅ Action performedReview finished.
|
…te macro (#674) Adds the proc-macro crate runall uses to re-expose every per-stage option of `fgumi sort` / `group` / `simplex` / `duplex` / `codec` without hand-maintaining a parallel option set. `#[multi_options("sort", "Sort Options")]` takes a `clap::Args` options struct and generates a `MultiSortOptions` companion whose flags are named `--sort::<flag>`, plus `validate()`, `TryFrom` and `From` conversions. The companion is faithful to the standalone command by construction: * every `default_value*` attribute is copied verbatim, so the two flags cannot advertise different defaults and the field type needs no `Display` impl; * long aliases are re-prefixed and short flags dropped, so nothing un-namespaced reaches the parent command; * each generated arg carries its own `help_heading`, so a parent argument declared after a flattened companion keeps its own heading; * `#[doc]` attributes are forwarded individually, preserving clap's short/long help split; * `#[cfg]` gates are forwarded onto the field and both conversion arms; * `#[arg(skip)]` fields ride along as skip fields, making `From` + `validate()` a lossless round trip; * `Vec<T>` passes through untouched -- clap never consults a struct's `Default`, so backfilling from it would diverge from the standalone command. Required-ness stays staged: `required` is dropped from the clap side and enforced by `validate()`, so a missing value is reported as `--sort::max-memory is required when sort is selected` rather than by clap's parser, which cannot name the stage. Forms the macro cannot re-expose faithfully fail the build with a spanned `syn::Error` pointing at the offending field: cross-field reference keys whose arg ids would dangle after prefixing, `id`/`name` overrides, clap's `key(value)` call form for classified keys, field- and struct-level `#[command(...)]`, `#[group(...)]`, an unenforceable `required`, a non-identifier prefix, and the legacy `#[clap(...)]` / `#[structopt(...)]` spellings that every classifier would otherwise silently ignore. Tests include parity fixtures carrying the `#[arg(...)]` attributes of the real `SortOptions` and `GroupOptions` verbatim, asserting the standalone and prefixed commands parse to identical structs, plus ten trybuild cases pinning the diagnostics. Also adds fgumi-cli-macros, fgumi-fmt and fgumi-cli-common to publish.yml's CRATES list. The latter two are publishable but were never listed, which fails that workflow's own completeness check on push to main.
…te macro (#674) Adds the proc-macro crate runall uses to re-expose every per-stage option of `fgumi sort` / `group` / `simplex` / `duplex` / `codec` without hand-maintaining a parallel option set. `#[multi_options("sort", "Sort Options")]` takes a `clap::Args` options struct and generates a `MultiSortOptions` companion whose flags are named `--sort::<flag>`, plus `validate()`, `TryFrom` and `From` conversions. The companion is faithful to the standalone command by construction: * every `default_value*` attribute is copied verbatim, so the two flags cannot advertise different defaults and the field type needs no `Display` impl; * long aliases are re-prefixed and short flags dropped, so nothing un-namespaced reaches the parent command; * each generated arg carries its own `help_heading`, so a parent argument declared after a flattened companion keeps its own heading; * `#[doc]` attributes are forwarded individually, preserving clap's short/long help split; * `#[cfg]` gates are forwarded onto the field and both conversion arms; * `#[arg(skip)]` fields ride along as skip fields, making `From` + `validate()` a lossless round trip; * `Vec<T>` passes through untouched -- clap never consults a struct's `Default`, so backfilling from it would diverge from the standalone command. Required-ness stays staged: `required` is dropped from the clap side and enforced by `validate()`, so a missing value is reported as `--sort::max-memory is required when sort is selected` rather than by clap's parser, which cannot name the stage. Forms the macro cannot re-expose faithfully fail the build with a spanned `syn::Error` pointing at the offending field: cross-field reference keys whose arg ids would dangle after prefixing, `id`/`name` overrides, clap's `key(value)` call form for classified keys, field- and struct-level `#[command(...)]`, `#[group(...)]`, an unenforceable `required`, a non-identifier prefix, and the legacy `#[clap(...)]` / `#[structopt(...)]` spellings that every classifier would otherwise silently ignore. Tests include parity fixtures carrying the `#[arg(...)]` attributes of the real `SortOptions` and `GroupOptions` verbatim, asserting the standalone and prefixed commands parse to identical structs, plus ten trybuild cases pinning the diagnostics. Also adds fgumi-cli-macros, fgumi-fmt and fgumi-cli-common to publish.yml's CRATES list. The latter two are publishable but were never listed, which fails that workflow's own completeness check on push to main.
…te macro (#674) Adds the proc-macro crate runall uses to re-expose every per-stage option of `fgumi sort` / `group` / `simplex` / `duplex` / `codec` without hand-maintaining a parallel option set. `#[multi_options("sort", "Sort Options")]` takes a `clap::Args` options struct and generates a `MultiSortOptions` companion whose flags are named `--sort::<flag>`, plus `validate()`, `TryFrom` and `From` conversions. The companion is faithful to the standalone command by construction: * every `default_value*` attribute is copied verbatim, so the two flags cannot advertise different defaults and the field type needs no `Display` impl; * long aliases are re-prefixed and short flags dropped, so nothing un-namespaced reaches the parent command; * each generated arg carries its own `help_heading`, so a parent argument declared after a flattened companion keeps its own heading; * `#[doc]` attributes are forwarded individually, preserving clap's short/long help split; * `#[cfg]` gates are forwarded onto the field and both conversion arms; * `#[arg(skip)]` fields ride along as skip fields, making `From` + `validate()` a lossless round trip; * `Vec<T>` passes through untouched -- clap never consults a struct's `Default`, so backfilling from it would diverge from the standalone command. Required-ness stays staged: `required` is dropped from the clap side and enforced by `validate()`, so a missing value is reported as `--sort::max-memory is required when sort is selected` rather than by clap's parser, which cannot name the stage. Forms the macro cannot re-expose faithfully fail the build with a spanned `syn::Error` pointing at the offending field: cross-field reference keys whose arg ids would dangle after prefixing, `id`/`name` overrides, clap's `key(value)` call form for classified keys, field- and struct-level `#[command(...)]`, `#[group(...)]`, an unenforceable `required`, a non-identifier prefix, and the legacy `#[clap(...)]` / `#[structopt(...)]` spellings that every classifier would otherwise silently ignore. Tests include parity fixtures carrying the `#[arg(...)]` attributes of the real `SortOptions` and `GroupOptions` verbatim, asserting the standalone and prefixed commands parse to identical structs, plus ten trybuild cases pinning the diagnostics. Also adds fgumi-cli-macros, fgumi-fmt and fgumi-cli-common to publish.yml's CRATES list. The latter two are publishable but were never listed, which fails that workflow's own completeness check on push to main.
…te macro (#674) Adds the proc-macro crate runall uses to re-expose every per-stage option of `fgumi sort` / `group` / `simplex` / `duplex` / `codec` without hand-maintaining a parallel option set. `#[multi_options("sort", "Sort Options")]` takes a `clap::Args` options struct and generates a `MultiSortOptions` companion whose flags are named `--sort::<flag>`, plus `validate()`, `TryFrom` and `From` conversions. The companion is faithful to the standalone command by construction: * every `default_value*` attribute is copied verbatim, so the two flags cannot advertise different defaults and the field type needs no `Display` impl; * long aliases are re-prefixed and short flags dropped, so nothing un-namespaced reaches the parent command; * each generated arg carries its own `help_heading`, so a parent argument declared after a flattened companion keeps its own heading; * `#[doc]` attributes are forwarded individually, preserving clap's short/long help split; * `#[cfg]` gates are forwarded onto the field and both conversion arms; * `#[arg(skip)]` fields ride along as skip fields, making `From` + `validate()` a lossless round trip; * `Vec<T>` passes through untouched -- clap never consults a struct's `Default`, so backfilling from it would diverge from the standalone command. Required-ness stays staged: `required` is dropped from the clap side and enforced by `validate()`, so a missing value is reported as `--sort::max-memory is required when sort is selected` rather than by clap's parser, which cannot name the stage. Forms the macro cannot re-expose faithfully fail the build with a spanned `syn::Error` pointing at the offending field: cross-field reference keys whose arg ids would dangle after prefixing, `id`/`name` overrides, clap's `key(value)` call form for classified keys, field- and struct-level `#[command(...)]`, `#[group(...)]`, an unenforceable `required`, a non-identifier prefix, and the legacy `#[clap(...)]` / `#[structopt(...)]` spellings that every classifier would otherwise silently ignore. Tests include parity fixtures carrying the `#[arg(...)]` attributes of the real `SortOptions` and `GroupOptions` verbatim, asserting the standalone and prefixed commands parse to identical structs, plus ten trybuild cases pinning the diagnostics. Also adds fgumi-cli-macros, fgumi-fmt and fgumi-cli-common to publish.yml's CRATES list. The latter two are publishable but were never listed, which fails that workflow's own completeness check on push to main.
Summary
Second PR in the
main-runallstack. Addscrates/fgumi-cli-macros, the proc-macro craterunalluses to re-expose every per-stage option offgumi sort/group/simplex/duplex/codecwithout hand-maintaining a parallel option set onRunAll.#[multi_options("sort", "Sort Options")]takes aclap::Argsoptions struct and generates aMultiSortOptionscompanion whose flags are named--sort::<flag>and filed under a per-stage help heading, plusvalidate(),TryFrom<MultiSortOptions>andFrom<SortOptions>conversions. The annotated struct is emitted unchanged.The contract
The companion is faithful to the standalone command by construction rather than by vigilance — the guiding principle for every design choice here:
default_value,default_value_t,default_value_os{,_t},default_values*Displayimpllong--<prefix>::<flag>, honoring an explicitlong = "..."overridealias,aliases,visible_alias,visible_aliasesshort,short_alias,visible_short_alias, …help_headinghelp,long_helphelpon a required field replaces the generatedRequired when <stage> is selected.line, since clap prefershelpover#[doc]; the stagedvalidate()error still names the flag and the stagerequiredvalidate()(see below)#[doc]#[cfg]#[cfg_attr]#[cfg]is valid on a struct-expression field#[arg(skip)]From+validate()losslessvalue_parser,action,num_args,hide,env, …)Two consequences worth calling out explicitly, since both are behaviour differences from the version of this macro on
feat-runall:Vec<T>passes through untouched. The old macro backfilled an emptyVecfromStruct::default(). clap never consults a struct'sDefault, so the standalone command yields an emptyVecwhen the flag is omitted — the backfill made the re-exposed flag diverge from the command it mirrors, and silently replaced an explicitly-emptyVec. Pinned byomitted_vec_matches_the_standalone_command_not_the_struct_default.requiredis enforced byvalidate(), not by clap's parser, so a missing value is reported as--sort::max-memory is required when sort is selected— naming the stage, which clap cannot do.requiredon anOption<T>orVec<T>is now propagated intovalidate()instead of being dropped with no compensating check.One detail of the generated code worth knowing: the companion carries
#[command(about = None, long_about = None)]. clap adopts a flattenedArgsstruct's doc comment as the parent command'saboutwhen the parent declares none, so without this the companion's rustdoc would surface asrunall's description — and with several stages flattened, clap would arbitrarily pick whichever came first. Dropping the rustdoc instead is not an option: it is what docs.rs renders, and crates in this workspace#![deny(missing_docs)]. A parent that documents itself keeps its own description either way.Fail-loud rejections
Anything the macro cannot re-expose faithfully is a build error with a spanned
syn::Errorpointing at the offending field, attribute or literal — never a silent misclassification, and no longer apanic!that points at the attribute and reports only the first problem:requires,conflicts_with,required_if_eq, … — all 20) whose arg ids would dangle once fields are prefixedid/nameoverrides, which would reintroduce the un-prefixed name#[arg(index = …)]or by declaring neitherlongnorshort. A positional has no flag name to prefix, several stages' positionals would be mutually ambiguous, and clap panics outright when a positional carries thelongthe companion always emits (Argument 'pos_input' is a positional argument and can't have short or long name versions)key(value)call form for any classified key — it arrives as aMeta::Listand would slip past every classifier#[cfg_attr]. The macro classifies from literal#[arg(...)]attributes and cannot evaluate a cfg predicate, so#[cfg_attr(unix, arg(long, default_value_t = 3))]would be ignored, the field classified required, wrapped inOption<T>, and the forwarded attribute then applied to the wrapper — a wall of type errors that never mentionsmulti_options#[command(...)], and struct-level#[group(...)]. A struct-level setting would apply to the standalone command and silently not to the same options re-exposed onrunall; none of the structsrunallannotates carries one todaywhereclause. The companion and both conversion impls are emitted without generic parameters, so the expansion cannot compile, and every resulting error names the type parameter rather than this macrorequiredon a field that always holds a value (defaulted, barebool, or skipped), where it would be unenforceableformat_ident!panic with no mention ofmulti_options#[clap(...)]/#[structopt(...)]spellings, which every classifier keys past — a#[clap(skip)]field would have been exposed as a required CLI flagTest plan
7,173 tests pass (27 skipped), up from 7,021 on the base.
ci-fmt,ci-lint,ci-doctestandci-docare green; patch coverage is 97.89% against the 90% gate.The suite that matters most is
tests/real_world.rs: fixtures carrying the#[arg(...)]attributes of the realSortOptionsandGroupOptionsverbatim (only the value types are local stand-ins), asserting that the standalone command and the prefixed companion parse to identical structs — for defaults and for supplied values. That covers every classification rule at once: a dropped default, a misclassifiedbool, a leaked alias or a lostvalue_parserall surface as a field that disagrees. It also builds a two-stagerunall-shaped command to prove the stages coexist without colliding.tests/behavior.rspins each contract property individually, and fourteentrybuildcases pin the diagnostics with field-accurate spans. Every fix was verified discriminating by reverting it and confirming the matching test fails.Two table-drift guards are worth noting because they protect the design rather than a behavior:
every_classified_key_is_call_form_sensitiveasserts that every key the classifier reads or rewrites also appears inCALL_FORM_SENSITIVE_ARG_KEYS(adding adefault_value*spelling without it would silently reopen the call-form hole for that key), andevery_cross_reference_key_in_the_table_is_rejecteddrives the constant itself rather than a hand-maintained copy.Also in this PR
publish.yml's hardcodedCRATESlist gainsfgumi-cli-macros— andfgumi-fmtandfgumi-cli-common, which PR #672 added but never listed. All three are publishable, so the workflow's own completeness check fails on push tomainwithout them.publish-dry-rundoes not catch this: it runscargo publish --workspace --dry-run, which never readspublish.yml. Verified locally by running the workflow's completeness, stale-entry and topological-order checks against the new array.Reading order
crates/fgumi-cli-macros/src/lib.rs— crate docs first; they state the whole contractcrates/fgumi-cli-macros/tests/real_world.rs— the parity assertionscrates/fgumi-cli-macros/tests/ui/*.stderr— the diagnostics as a reviewer would see themSummary by CodeRabbit
New Features
Tests