From 48caedc06059e163d599f74dabde59a65d15e6b8 Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sun, 2 Aug 2026 13:49:59 +0900 Subject: [PATCH] refactor(lib)!: mark ParseOutput and UsageErr non_exhaustive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten public types in usage-lib already carry `#[non_exhaustive]` — `Spec`, `SpecArg`, `SpecFlag`, `Parser` among them. These two were missed, and #762 ran into the consequence: adding a variant to `UsageErr` and two fields to `ParseOutput` were each reported as breaking on their own. Neither type is one a caller builds or matches exhaustively. `ParseOutput` is what `parse()` returns; its only struct literal is in `parse.rs` itself, and nothing outside the crate constructs one. `UsageErr` is an error type, where a wildcard arm is the normal shape. Nothing in the workspace matches it — the CLI uses it as a return type and downcasts once. This also restores a signal that was lost. The squash of #762 took its subject from the PR title, which had no `!`, so the `BREAKING CHANGE:` footer on the branch commit never reached main: fix(parse): enforce double_dash="required" for positional args (#762) git-cliff therefore reads main as holding only a `fix:` since v4.1.0 and would compute v4.1.1 for the next release — shipping two breaking API changes as a patch. Marking a type `#[non_exhaustive]` is itself a breaking change, so the `!` here is the honest description of this commit, and it puts the major back within reach of `--bumped-version`. The version in Cargo.toml is deliberately untouched: `tasks/release-plz` publishes and tags as soon as main carries a version with no matching tag, so a hand-written bump in a feature PR would publish on the merge push with no changelog entry and no release PR. `lint:semver` therefore stays red until the release PR bumps it, which is the state main has been in since #762 landed. BREAKING CHANGE: `ParseOutput` can no longer be constructed with a struct literal outside usage-lib, and `UsageErr` can no longer be matched exhaustively outside it — a wildcard arm is now required. Both replace the narrower breaks #762 introduced with the durable form. --- lib/src/error.rs | 1 + lib/src/parse.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/src/error.rs b/lib/src/error.rs index 336695ef..70cb1b10 100644 --- a/lib/src/error.rs +++ b/lib/src/error.rs @@ -2,6 +2,7 @@ use miette::{Diagnostic, NamedSource, SourceSpan}; use thiserror::Error; #[derive(Error, Diagnostic, Debug)] +#[non_exhaustive] pub enum UsageErr { #[error("Invalid flag `{token}`: {reason}")] InvalidFlag { diff --git a/lib/src/parse.rs b/lib/src/parse.rs index 2c0eb40c..51cb982b 100644 --- a/lib/src/parse.rs +++ b/lib/src/parse.rs @@ -238,6 +238,7 @@ fn get_flag_key(word: &str) -> &str { } } +#[non_exhaustive] pub struct ParseOutput { pub cmd: SpecCommand, pub cmds: Vec,