The admin CLI already has a clean two-trait story for running commands --
Dispatch on a command group (derived, so the compiler forces every variant
to be runnable) and Run on each leaf -- but a handful of commands grew up
outside it: some hand-roll their own Dispatch, a couple skip Run entirely,
and the two biggest subtrees (mlx and measured-boot) thread their own context
structs through bespoke cmds::dispatch fns. This pulls them all back onto the
traits so there's exactly one way a command runs, and the derive does the
enforcing.
What this involves
ping, version: add the missing impl Run -- today they hand-write
Dispatch and skip Run, unlike inventory/jump/generate-*.
- A shared bridge: a tiny
dispatch_via_run! macro in cfg/dispatch.rs,
routing the seven subcommand-less top-levels (inventory, jump, ping,
version, generate-docs, generate-man, generate-shell-complete)
through it -- deleting seven copies of the same impl Dispatch { self.run() }.
scout_stream: hand-written impl Dispatch -> #[derive(Dispatch)],
with an impl Run per leaf; drop the local CliContext.
mlx: convert MlxAction + its six subcommand enums to
#[derive(Dispatch)], give every leaf impl Run, retire the custom
CliContext (just &api_client + &format, both on RuntimeContext).
Keep the shared table/print helpers in mlx/mod.rs.
- measured-boot: same across its six subgroups; retire
CliData /
GlobalOptions, preserving the one-time set_summary(!extended) side-effect
at the group dispatch point.
- Naming: rename the single-command
cmds.rs -> cmd.rs to match the ~254
other leaves. Leave genuinely-plural cmds.rs (mlx/, measured_boot/,
redfish, rms, debug_bundle) alone.
- Exempt by design:
redfish and rms run before the API client exists
(own BMC/RMS clients from raw CliOptions, never see RuntimeContext), so
they stay outside the trait flow -- main.rs already special-cases them.
A code comment will say so.
- No behavior change: clap surface, help, and output identical;
cargo make check-cli-docs should be a no-op.
Follow-up: once every group is on the derive, a custom lint/test could ban any
hand-written impl Dispatch, making the traits truly mandatory.
The admin CLI already has a clean two-trait story for running commands --
Dispatchon a command group (derived, so the compiler forces every variantto be runnable) and
Runon each leaf -- but a handful of commands grew upoutside it: some hand-roll their own
Dispatch, a couple skipRunentirely,and the two biggest subtrees (
mlxand measured-boot) thread their own contextstructs through bespoke
cmds::dispatchfns. This pulls them all back onto thetraits so there's exactly one way a command runs, and the derive does the
enforcing.
What this involves
ping,version: add the missingimpl Run-- today they hand-writeDispatchand skipRun, unlikeinventory/jump/generate-*.dispatch_via_run!macro incfg/dispatch.rs,routing the seven subcommand-less top-levels (
inventory,jump,ping,version,generate-docs,generate-man,generate-shell-complete)through it -- deleting seven copies of the same
impl Dispatch { self.run() }.scout_stream: hand-writtenimpl Dispatch->#[derive(Dispatch)],with an
impl Runper leaf; drop the localCliContext.mlx: convertMlxAction+ its six subcommand enums to#[derive(Dispatch)], give every leafimpl Run, retire the customCliContext(just&api_client+&format, both onRuntimeContext).Keep the shared table/print helpers in
mlx/mod.rs.CliData/GlobalOptions, preserving the one-timeset_summary(!extended)side-effectat the group dispatch point.
cmds.rs->cmd.rsto match the ~254other leaves. Leave genuinely-plural
cmds.rs(mlx/, measured_boot/,redfish, rms, debug_bundle) alone.
redfishandrmsrun before the API client exists(own BMC/RMS clients from raw
CliOptions, never seeRuntimeContext), sothey stay outside the trait flow --
main.rsalready special-cases them.A code comment will say so.
cargo make check-cli-docsshould be a no-op.Follow-up: once every group is on the derive, a custom lint/test could ban any
hand-written
impl Dispatch, making the traits truly mandatory.