Skip to content

feat(cli-macros): add fgumi-cli-macros with the multi_options attribute macro - #674

Merged
nh13 merged 1 commit into
main-runallfrom
nh/runall-02-cli-macros
Aug 1, 2026
Merged

feat(cli-macros): add fgumi-cli-macros with the multi_options attribute macro#674
nh13 merged 1 commit into
main-runallfrom
nh/runall-02-cli-macros

Conversation

@nh13

@nh13 nh13 commented Jul 31, 2026

Copy link
Copy Markdown
Member

Summary

Second PR in the main-runall stack. Adds crates/fgumi-cli-macros, 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 on RunAll.

#[multi_options("sort", "Sort Options")] takes a clap::Args options struct and generates a MultiSortOptions companion whose flags are named --sort::<flag> and filed under a per-stage help heading, plus validate(), TryFrom<MultiSortOptions> and From<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:

Attribute Treatment
default_value, default_value_t, default_value_os{,_t}, default_values* copied verbatim — the two flags cannot advertise different defaults, and the field type needs no Display impl
long re-prefixed to --<prefix>::<flag>, honoring an explicit long = "..." override
alias, aliases, visible_alias, visible_aliases re-prefixed the same way, so nothing un-namespaced reaches the parent command
short, short_alias, visible_short_alias, … dropped — one letter cannot be namespaced per stage
help_heading dropped — the companion files every field under its stage's heading; a field-level one is emitted after it and wins, so the field would escape its stage, and two stages declaring the same heading would merge into one section naming neither
help, long_help copied verbatim — the field's own documentation, identical on both commands. One consequence: an explicit help on a required field replaces the generated Required when <stage> is selected. line, since clap prefers help over #[doc]; the staged validate() error still names the flag and the stage
required dropped from the clap side, enforced by validate() (see below)
#[doc] forwarded one attribute at a time, so clap's short/long help split survives
#[cfg] forwarded onto the field and both conversion arms, so a compiled-out field is absent from all three
#[cfg_attr] forwarded onto the field only — it never removes a field, and only #[cfg] is valid on a struct-expression field
#[arg(skip)] carried onto the companion as a skip field, making From + validate() lossless
everything else (value_parser, action, num_args, hide, env, …) copied verbatim

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 empty Vec from Struct::default(). clap never consults a struct's Default, so the standalone command yields an empty Vec when the flag is omitted — the backfill made the re-exposed flag diverge from the command it mirrors, and silently replaced an explicitly-empty Vec. Pinned by omitted_vec_matches_the_standalone_command_not_the_struct_default.
  • Required-ness stays staged. required is enforced by validate(), 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. required on an Option<T> or Vec<T> is now propagated into validate() 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 flattened Args struct's doc comment as the parent command's about when the parent declares none, so without this the companion's rustdoc would surface as runall'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::Error pointing at the offending field, attribute or literal — never a silent misclassification, and no longer a panic! that points at the attribute and reports only the first problem:

  • cross-field reference keys (requires, conflicts_with, required_if_eq, … — all 20) whose arg ids would dangle once fields are prefixed
  • id / name overrides, which would reintroduce the un-prefixed name
  • positional arguments, whether declared by #[arg(index = …)] or by declaring neither long nor short. A positional has no flag name to prefix, several stages' positionals would be mutually ambiguous, and clap panics outright when a positional carries the long the companion always emits (Argument 'pos_input' is a positional argument and can't have short or long name versions)
  • clap's key(value) call form for any classified key — it arrives as a Meta::List and would slip past every classifier
  • a clap attribute hidden behind #[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 in Option<T>, and the forwarded attribute then applied to the wrapper — a wall of type errors that never mentions multi_options
  • field-level and struct-level #[command(...)], and struct-level #[group(...)]. A struct-level setting would apply to the standalone command and silently not to the same options re-exposed on runall; none of the structs runall annotates carries one today
  • generic structs — a type parameter or a where clause. 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 macro
  • required on a field that always holds a value (defaulted, bare bool, or skipped), where it would be unenforceable
  • a non-identifier prefix — previously an opaque format_ident! panic with no mention of multi_options
  • the legacy #[clap(...)] / #[structopt(...)] spellings, which every classifier keys past — a #[clap(skip)] field would have been exposed as a required CLI flag

Test plan

7,173 tests pass (27 skipped), up from 7,021 on the base. ci-fmt, ci-lint, ci-doctest and ci-doc are 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 real SortOptions and GroupOptions verbatim (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 misclassified bool, a leaked alias or a lost value_parser all surface as a field that disagrees. It also builds a two-stage runall-shaped command to prove the stages coexist without colliding.

tests/behavior.rs pins each contract property individually, and fourteen trybuild cases 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_sensitive asserts that every key the classifier reads or rewrites also appears in CALL_FORM_SENSITIVE_ARG_KEYS (adding a default_value* spelling without it would silently reopen the call-form hole for that key), and every_cross_reference_key_in_the_table_is_rejected drives the constant itself rather than a hand-maintained copy.

Also in this PR

publish.yml's hardcoded CRATES list gains fgumi-cli-macrosand fgumi-fmt and fgumi-cli-common, which PR #672 added but never listed. All three are publishable, so the workflow's own completeness check fails on push to main without them. publish-dry-run does not catch this: it runs cargo publish --workspace --dry-run, which never reads publish.yml. Verified locally by running the workflow's completeness, stale-entry and topological-order checks against the new array.

Reading order

  1. crates/fgumi-cli-macros/src/lib.rs — crate docs first; they state the whole contract
  2. crates/fgumi-cli-macros/tests/real_world.rs — the parity assertions
  3. crates/fgumi-cli-macros/tests/ui/*.stderr — the diagnostics as a reviewer would see them
  4. everything else

Summary by CodeRabbit

  • New Features

    • Added support for composing reusable command-line option groups with prefixed arguments.
    • Preserves defaults, aliases, help text, flags, repeated values, optional fields, and skipped fields.
    • Adds staged validation and conversion between grouped options and validated application settings.
    • Provides clear diagnostics for unsupported configurations and invalid option definitions.
  • Tests

    • Added comprehensive behavioral, real-world, smoke, and compile-failure coverage for option composition and validation.

@nh13
nh13 temporarily deployed to github-actions July 31, 2026 02:07 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c46ac063-1534-4f09-b477-b5df986dee15

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR adds the fgumi-cli-macros proc-macro crate. It implements multi_options, generates prefixed Clap argument structs, provides staged validation and conversions, updates workspace publishing, and adds behavioral and compile-fail tests.

Changes

multi_options macro

Layer / File(s) Summary
Workspace and publish integration
.github/workflows/publish.yml, Cargo.toml, crates/fgumi-cli-macros/Cargo.toml
The workspace includes the proc-macro crate, defines its dependencies, and publishes workspace crates in dependency order while reporting actual publication status.
Macro expansion and validation
crates/fgumi-cli-macros/src/lib.rs
multi_options generates visibility-matched Multi<Name> Clap structs with prefixed flags, conversions, staged required-field validation, attribute rewriting, and diagnostics for unsupported configurations.
Runtime behavior contracts
crates/fgumi-cli-macros/tests/behavior.rs, crates/fgumi-cli-macros/tests/smoke.rs, crates/fgumi-cli-macros/tests/real_world.rs
Tests cover defaults, aliases, help output, skipped fields, vectors, booleans, required values, conversions, parser enforcement, and multi-stage command composition.
Compile-fail diagnostic coverage
crates/fgumi-cli-macros/tests/compile_fail.rs, crates/fgumi-cli-macros/tests/ui/*
Trybuild tests cover invalid prefixes, unsupported structures and attributes, positional fields, dangling references, visibility, generics, and unenforceable requirements.

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
Loading

Possibly related PRs

  • fulcrumgenomics/fgumi#542: Both changes implement and validate multi_options, including unsupported cross-reference and call-form attributes.
  • fulcrumgenomics/fgumi#672: Both changes modify workspace crate membership and shared dependency wiring in Cargo.toml.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the new fgumi-cli-macros crate and its primary multi_options attribute macro.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nh/runall-02-cli-macros

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.89104% with 12 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main-runall@8238be9). Learn more about missing BASE report.

Files with missing lines Patch % Lines
crates/fgumi-cli-macros/src/lib.rs 97.89% 12 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8238be9 and 3c2894c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (32)
  • .github/workflows/publish.yml
  • Cargo.toml
  • crates/fgumi-cli-macros/Cargo.toml
  • crates/fgumi-cli-macros/src/lib.rs
  • crates/fgumi-cli-macros/tests/behavior.rs
  • crates/fgumi-cli-macros/tests/compile_fail.rs
  • crates/fgumi-cli-macros/tests/real_world.rs
  • crates/fgumi-cli-macros/tests/smoke.rs
  • crates/fgumi-cli-macros/tests/ui/bad_prefix.rs
  • crates/fgumi-cli-macros/tests/ui/bad_prefix.stderr
  • crates/fgumi-cli-macros/tests/ui/call_form_long.rs
  • crates/fgumi-cli-macros/tests/ui/call_form_long.stderr
  • crates/fgumi-cli-macros/tests/ui/command_flatten.rs
  • crates/fgumi-cli-macros/tests/ui/command_flatten.stderr
  • crates/fgumi-cli-macros/tests/ui/conditional_clap_attr.rs
  • crates/fgumi-cli-macros/tests/ui/conditional_clap_attr.stderr
  • crates/fgumi-cli-macros/tests/ui/cross_reference_arg.rs
  • crates/fgumi-cli-macros/tests/ui/cross_reference_arg.stderr
  • crates/fgumi-cli-macros/tests/ui/legacy_clap_attr.rs
  • crates/fgumi-cli-macros/tests/ui/legacy_clap_attr.stderr
  • crates/fgumi-cli-macros/tests/ui/non_struct.rs
  • crates/fgumi-cli-macros/tests/ui/non_struct.stderr
  • crates/fgumi-cli-macros/tests/ui/positional_field.rs
  • crates/fgumi-cli-macros/tests/ui/positional_field.stderr
  • crates/fgumi-cli-macros/tests/ui/private_visibility.rs
  • crates/fgumi-cli-macros/tests/ui/private_visibility.stderr
  • crates/fgumi-cli-macros/tests/ui/struct_level_command.rs
  • crates/fgumi-cli-macros/tests/ui/struct_level_command.stderr
  • crates/fgumi-cli-macros/tests/ui/tuple_struct.rs
  • crates/fgumi-cli-macros/tests/ui/tuple_struct.stderr
  • crates/fgumi-cli-macros/tests/ui/unenforceable_required.rs
  • crates/fgumi-cli-macros/tests/ui/unenforceable_required.stderr

Comment thread crates/fgumi-cli-macros/src/lib.rs
Comment thread crates/fgumi-cli-macros/src/lib.rs
Comment thread crates/fgumi-cli-macros/tests/behavior.rs
Comment thread crates/fgumi-cli-macros/tests/compile_fail.rs Outdated
Comment thread crates/fgumi-cli-macros/tests/real_world.rs
@nh13

nh13 commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews paused.

@nh13
nh13 force-pushed the nh/runall-02-cli-macros branch from 3c2894c to 0761b94 Compare July 31, 2026 03:53
@nh13
nh13 temporarily deployed to github-actions July 31, 2026 03:53 — with GitHub Actions Inactive
@nh13

nh13 commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3c2894c and ade5592.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (36)
  • .github/workflows/publish.yml
  • Cargo.toml
  • crates/fgumi-cli-macros/Cargo.toml
  • crates/fgumi-cli-macros/src/lib.rs
  • crates/fgumi-cli-macros/tests/behavior.rs
  • crates/fgumi-cli-macros/tests/compile_fail.rs
  • crates/fgumi-cli-macros/tests/real_world.rs
  • crates/fgumi-cli-macros/tests/smoke.rs
  • crates/fgumi-cli-macros/tests/ui/bad_prefix.rs
  • crates/fgumi-cli-macros/tests/ui/bad_prefix.stderr
  • crates/fgumi-cli-macros/tests/ui/call_form_long.rs
  • crates/fgumi-cli-macros/tests/ui/call_form_long.stderr
  • crates/fgumi-cli-macros/tests/ui/command_flatten.rs
  • crates/fgumi-cli-macros/tests/ui/command_flatten.stderr
  • crates/fgumi-cli-macros/tests/ui/conditional_clap_attr.rs
  • crates/fgumi-cli-macros/tests/ui/conditional_clap_attr.stderr
  • crates/fgumi-cli-macros/tests/ui/cross_reference_arg.rs
  • crates/fgumi-cli-macros/tests/ui/cross_reference_arg.stderr
  • crates/fgumi-cli-macros/tests/ui/generic_struct.rs
  • crates/fgumi-cli-macros/tests/ui/generic_struct.stderr
  • crates/fgumi-cli-macros/tests/ui/legacy_clap_attr.rs
  • crates/fgumi-cli-macros/tests/ui/legacy_clap_attr.stderr
  • crates/fgumi-cli-macros/tests/ui/non_struct.rs
  • crates/fgumi-cli-macros/tests/ui/non_struct.stderr
  • crates/fgumi-cli-macros/tests/ui/positional_field.rs
  • crates/fgumi-cli-macros/tests/ui/positional_field.stderr
  • crates/fgumi-cli-macros/tests/ui/private_visibility.rs
  • crates/fgumi-cli-macros/tests/ui/private_visibility.stderr
  • crates/fgumi-cli-macros/tests/ui/struct_level_command.rs
  • crates/fgumi-cli-macros/tests/ui/struct_level_command.stderr
  • crates/fgumi-cli-macros/tests/ui/tuple_struct.rs
  • crates/fgumi-cli-macros/tests/ui/tuple_struct.stderr
  • crates/fgumi-cli-macros/tests/ui/unenforceable_required.rs
  • crates/fgumi-cli-macros/tests/ui/unenforceable_required.stderr
  • crates/fgumi-cli-macros/tests/ui/where_only.rs
  • crates/fgumi-cli-macros/tests/ui/where_only.stderr

Comment thread .github/workflows/publish.yml
Comment thread crates/fgumi-cli-macros/tests/behavior.rs
Comment thread crates/fgumi-cli-macros/tests/behavior.rs
Comment thread crates/fgumi-cli-macros/tests/behavior.rs
Comment thread crates/fgumi-cli-macros/tests/real_world.rs
Comment thread crates/fgumi-cli-macros/tests/real_world.rs
…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.
@nh13
nh13 force-pushed the nh/runall-02-cli-macros branch from ade5592 to aca8c9a Compare July 31, 2026 15:54
@nh13
nh13 temporarily deployed to github-actions July 31, 2026 15:54 — with GitHub Actions Inactive
@nh13

nh13 commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13
nh13 merged commit 7576f13 into main-runall Aug 1, 2026
14 checks passed
@nh13
nh13 deleted the nh/runall-02-cli-macros branch August 1, 2026 15:55
nh13 added a commit that referenced this pull request Aug 6, 2026
…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.
nh13 added a commit that referenced this pull request Aug 7, 2026
…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.
nh13 added a commit that referenced this pull request Aug 9, 2026
…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.
nh13 added a commit that referenced this pull request Aug 10, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant