Extends the effect= annotation from commands down to individual flags and arguments, so specs can express commands whose danger depends on how they are invoked.
Added
-
effect=on flags and args (#742 by @jdx). A command's danger often depends on how it is invoked:pitchfork logsreads,pitchfork logs --cleardeletes;mise settings fooreads,mise settings foo=barwrites. Flags and args can now carry the sameeffect=annotation that #739 introduced on commands:cmd "logs" effect="read" help="Show daemon logs" { flag "--clear" effect="destructive" help="Delete stored logs" flag "--follow" } cmd "settings" effect="read" { arg "[setting]" arg "[value]" effect="write" // `settings foo` reads, `settings foo=bar` writes }
The rule: the effect of an invocation is the maximum of the command's effect and the effect of every flag and argument actually supplied.
SpecCommandEffectnow implementsOrd(read<write<destructive) so the maximum is well defined. Two helpers:SpecCommand::effect_of(flags, args)— for a consumer that parsed the command line.SpecCommand::max_effect()— the pessimistic bound across every declared flag and arg, for one that didn't.
Effects only ever raise, never lower.
--dry-runis deliberately not supported: a bug in a dry-run path would otherwise produce a spec that claims a command is safe when it isn't. Because the rule is monotonic, a consumer that can't parse the invocation can safely fall back tomax_effect()and degrade to today's behavior — which is what makes this additive rather than a semantics break on the wire.Accepted as either a prop (
effect="write") or a child node (effect "write"); unknown values are a parse error with a span. Generated markdown now renders an**Effect**:line beneath each flag that declares one. Most flags should declare nothing — this is for the handful that change what a command does to the world, not an annotation for every option. -
New builder and constructor helpers (#742 by @jdx).
SpecFlagBuilderandSpecArgBuildergain.effect(...),.usage(...), and.help_first_line(...).SpecExample,SpecComplete,SpecConfig,SpecConfigProp, andSpecMountall gain a public::newand chaining setters, so they can be constructed from outside the crate.
Breaking Changes
SpecArg, SpecFlag, SpecExample, SpecComplete, SpecChoices, SpecConfig, SpecConfigProp, and SpecMount are now marked #[non_exhaustive]. Any code that built one of these types with a struct literal outside usage-lib will no longer compile. Use the builders (SpecFlag::builder(), SpecArg::builder()) or the new ::new helpers instead; existing field access is unaffected.
Full Changelog: v3.6.0...v4.0.0
💚 Sponsor usage
usage is maintained by @jdx, an open source developer for entire.io, the title sponsor of the jdx.dev open source tools including mise, aube, hk, and more. Work on usage is funded by sponsorships.
If usage powers CLI specs, docs, or completions for a tool you maintain or use, please consider sponsoring at jdx.dev. Every sponsorship helps the project stay independent and moving.