Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ warrant attribution:
- `usage-derive` / `usage-rs` deliberately mirror `clap_derive`'s attribute
vocabulary and semantics (`long`, `short`, `env`, `default_value`, `flatten`,
`value_enum`, `rename_all`, and friends) so a clap declaration can be ported
field by field. See [docs/rust/clap-compatibility.md](docs/rust/clap-compatibility.md).
field by field. See [docs/rust/migrating-from-clap.md](docs/rust/migrating-from-clap.md).
- The rendered help, usage line, and diagnostic conventions follow clap's output
shape so migrated CLIs keep their existing user-facing text.
- `clap_usage` reads a `clap::Command` through clap's public API to generate a
Expand Down
2 changes: 1 addition & 1 deletion conformance/tests/clap_micro.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Minimal paired CLIs that turn the clap compatibility matrix into executable claims.
//! Minimal paired CLIs that turn the clap migration claims into executable checks.

use std::ffi::OsStr;

Expand Down
1 change: 0 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export default defineConfig({
{ text: "Subcommands", link: "/rust/subcommands" },
{ text: "Dispatch", link: "/rust/dispatch" },
{ text: "Migrating from clap", link: "/rust/migrating-from-clap" },
{ text: "clap Compatibility", link: "/rust/clap-compatibility" },
{ text: "Validation", link: "/rust/validation" },
{ text: "Configuration", link: "/rust/configuration" },
{ text: "Help, Version, and Errors", link: "/rust/help" },
Expand Down
14 changes: 8 additions & 6 deletions docs/rust/args-and-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ Values are built with `FromStr`, so `PathBuf`, `usize`, `IpAddr`, and your own t
The `FromStr` error type must implement `Display` (a compile error names the type otherwise);
a conversion failure at runtime becomes `Error::InvalidValue { name, value, reason }`.

On Unix, `PathBuf` and `OsString` keep non-UTF-8 argv bytes unchanged. `String` fields report
invalid UTF-8 rather than replacing it. On Windows, values that cannot be converted safely are
reported instead of using an unchecked reconstruction.

A `Vec` flag is repeatable (`var` in spec terms) automatically. Two related attributes cover the
other shapes:

Expand All @@ -59,8 +63,7 @@ jobs: Option<u32>,

The tables below cover the attributes most CLIs need. Many clap-compatible spellings
(`visible_alias`, `hide_*`, `last`, `trailing_var_arg`, `requires_if`, and others) are also
accepted — see [Migrating from clap](/rust/migrating-from-clap) and the
[compatibility matrix](/rust/clap-compatibility) for the audited surface.
accepted — see [Migrating from clap](/rust/migrating-from-clap).

**Naming and shape** — what the field is called and what kind of argument it is:

Expand Down Expand Up @@ -89,7 +92,7 @@ accepted — see [Migrating from clap](/rust/migrating-from-clap) and the
| `delimiter = ','` | Split one word into several values ([Validation](/rust/validation#delimiters)) |
| `value_terminator = ";"` | End a variadic field at this token without storing the token |
| `bool_value` | Let a boolean long flag accept attached `=true` or `=false` |
| `value_optional` | Mark the value optional in help (help-only; the parser still wants one) |
| `value_optional` | Help/spec only; bind a bare flag with `default_missing` or `Option<Option<T>>` |

**Env vars and defaults** — where a value comes from when argv has none:

Expand Down Expand Up @@ -245,9 +248,8 @@ stdin: bool,

Naming a flag or positional that doesn't exist on the command is a **compile error**, not a
runtime surprise. Conflicts, requires, and conditional requiredness may name flags or
positionals; `overrides` and some `requires_if` forms stay flag-only. See the
[compatibility matrix](/rust/clap-compatibility#relationships-and-command-routing) for the
audited details.
positionals; `overrides` and some `requires_if` forms stay flag-only. See
[Compatibility gaps](/rust/migrating-from-clap#compatibility-gaps) for the migration details.

## Resolution order

Expand Down
173 changes: 14 additions & 159 deletions docs/rust/clap-compatibility.md

Large diffs are not rendered by default.

26 changes: 1 addition & 25 deletions docs/rust/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,31 +149,7 @@ how to opt out of the endpoint.
- [Help, version, and errors](/rust/help) — what the parser renders and how to hook it
- [Completions](/rust/completions) — static scripts and runtime completion
- [Configuration](/rust/configuration) — settings declared in code: `usage::Config` and layered resolution
- [Testing](/rust/testing) — assert parses, help pages, and completions with no process spawned
- [Testing](/rust/testing) — run commands or assert directly on parsing, help, and completions
- [Spec output](/rust/spec) — the emitted KDL and usage-cli integration
- [Migrating from clap](/rust/migrating-from-clap) — mechanical rewrites and intentional API breaks
- [clap compatibility](/rust/clap-compatibility) — supported behavior, bridge losses, and non-goals
- [Performance](/rust/performance) — what a parse costs, measured at mise's scale

## Current limitations

The framework intentionally targets standard GNU-style CLIs, and a few clap features have no
equivalent yet:

- `#[usage(value_optional)]` changes help and spec presentation only. To bind a bare flag, use
`default_missing` or an `Option<Option<T>>` field.
- Literal `value_parser = ["…"]` arrays become portable choices, but typed `value_parser`
callbacks are rejected because they cannot enter the spec. Other values use `FromStr`;
portable validation expressions require the `validation` feature.
- Long flags and subcommands require exact spellings. With `unknown_flags = "error"`,
diagnostics can suggest a close match, but parsing never accepts prefixes whose meaning could
change when another declaration is added.
- Completion scripts cover bash, fish, Nushell, PowerShell, and zsh. Elvish is not supported; a
clap application publishing an Elvish script must keep `clap_complete` for that one artifact.
- `help_template` uses six portable, pre-rendered sections rather than clap's finer-grained
template tags. Existing clap templates must be rewritten, and the clap bridge cannot recover
them because clap does not expose its template. See
[Laying a page out](/rust/help#laying-a-page-out).
- On Unix, `PathBuf` and `OsString` fields accept non-UTF-8 argv without changing a byte. String
fields still report invalid UTF-8 precisely rather than replacing it; on Windows, values that
cannot be converted safely are reported instead of using an unchecked reconstruction.
34 changes: 25 additions & 9 deletions docs/rust/migrating-from-clap.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,26 @@ The Rust framework is a typed parser with static metadata, not a compatibility l
clap. Most derive-based CLIs migrate mechanically. Builder APIs and `ArgMatches` are intentional
API breaks: move their behavior into typed declarations or keep clap at that boundary.

Use the [compatibility matrix](/rust/clap-compatibility) as the audited baseline. It separates
behavior supported by usage itself from metadata that `clap_usage` can recover from an existing
`clap::Command`.
## Compatibility gaps

Check these before starting a migration:

| Difference | What to do |
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Runtime `Command` builders, `ArgMatches`, and `CommandFactory` are not part of the typed API. | Move the declaration to derives, keep clap at that boundary, or use `usage-lib` for a CLI that is genuinely dynamic. |
| Unknown flags and repeated scalar flags are permissive by default. | Add `unknown_flags = "error"` and `args_override_self = false` where clap's strict behavior matters. |
| `from_global` is unsupported. | Read the global field from its declaring type and pass it to the command as context. |
| Typed `value_parser` callbacks cannot enter a portable spec. | Use the field type's `FromStr`, a `ValueEnum`, literal choices, or a portable `validate` expression. |
| `#[usage(value_optional)]` changes help and spec presentation only. | Bind a bare flag with `default_missing` or an `Option<Option<T>>` field. |
| Some relationships through `flatten` or on positionals are not available at binding time. | Keep a post-parse check for the cases described under [Subcommands and shared arguments](#subcommands-and-shared-arguments). |
| Prefix inference is intentionally unsupported. | Long flags and subcommands must use a full name or declared alias. |
| clap help templates and style palettes are not portable as-is. | Rewrite templates using usage's six [help sections](/rust/help#laying-a-page-out); usage chooses terminal styles automatically. |
| Completion generation does not target Elvish. | Keep `clap_complete` or another generator for that artifact. |

If a migration begins from a `clap::Command` rather than the Rust declaration,
`clap_usage::spec_with_report` detects recoverable losses. It cannot report state for which clap
exposes a setter but no getter, including the `requires` family, `default_value_if`, and
`default_missing_value`; audit those declarations directly.

## Dependencies

Expand Down Expand Up @@ -199,10 +216,8 @@ struct Explicit {
Relationships that cross a flattened boundary are **lossy**: common forms work, but a declaring
type cannot yet validate a selector supplied by a flattened sibling. Positional relationships
are **partial**: conflicts, requires, and conditional requiredness work; binding-time `overrides`
and value-source `requires_if` remain flag-only. Keep a post-parse check only for forms the
[compatibility matrix](/rust/clap-compatibility#relationships-and-command-routing) still marks
lossy or partial; the derive rejects selectors it can prove invalid instead of silently
weakening them.
and value-source `requires_if` remain flag-only. Keep a post-parse check for those cases; the
derive rejects selectors it can prove invalid instead of silently weakening them.

## Parse entry points

Expand Down Expand Up @@ -299,5 +314,6 @@ spellings may be added when they map to existing behavior. A spelling whose clap
be carried losslessly is rejected or documented as partial; it is not silently accepted with
weaker semantics.

The compatibility matrix is versioned against the clap releases named at its top. Updating clap
requires re-auditing that matrix; compiling with a new clap is not by itself a parity claim.
The paired conformance tests are audited against the clap versions in the workspace lockfile.
Updating clap requires re-running and reviewing those tests; compiling with a new clap is not by
itself a parity claim.
5 changes: 2 additions & 3 deletions docs/spec/integrations/clap.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ The report includes the command path, clap argument ID, feature, and source deta
for each detectable loss. `is_lossless()` therefore means lossless for behavior
visible through clap's public getters, not for every setter clap exposes. Before
treating the generated spec as fully compatible, audit the declaration against the
[compatibility matrix](/rust/clap-compatibility), especially its **usage-only** and
**lossy** bridge rows.
[clap migration guide](/rust/migrating-from-clap#compatibility-gaps).

## Integration Pattern

Expand Down Expand Up @@ -87,6 +86,6 @@ constraint. `Arg::default_value_if` is the same hole: a generated spec never car

## Links

- [clap compatibility matrix](/rust/clap-compatibility)
- [Migrating from clap](/rust/migrating-from-clap)
- [crate on crates.io](https://crates.io/crates/clap_usage)
- [source code](https://github.com/jdx/usage/tree/main/clap_usage)