Workaround for panic due to missing global validation in clap (#14368)

Clap does not perform global validation, so flag that are declared as
overriding can be set at the same time:
https://github.com/clap-rs/clap/issues/6049. This would previously cause
a panic. We work around this by choosing the yes-value always and
writing a warning.

An alternative would be erroring when both are set, but it's unclear to
me if this may break things we want to support. (`UV_OFFLINE=1 cargo run
-q pip --no-offline install tqdm --no-cache` is already banned).

Fixes https://github.com/astral-sh/uv/pull/14299

**Test Plan**

```
$ cargo run -q pip --offline install --no-offline tqdm --no-cache
  warning: Boolean flags on different levels are not correctly supported (https://github.com/clap-rs/clap/issues/6049)
    × No solution found when resolving dependencies:
    ╰─▶ Because tqdm was not found in the cache and you require tqdm, we can conclude that your requirements are unsatisfiable.

        hint: Packages were unavailable because the network was disabled. When the network is disabled, registry packages may only be read from the cache.
```
This commit is contained in:
konsti 2025-07-01 20:39:46 +02:00 committed by GitHub
parent 29fcd6faee
commit 06df95adbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 170 additions and 97 deletions

View file

@ -1,7 +1,10 @@
use anstream::eprintln;
use uv_cache::Refresh;
use uv_configuration::ConfigSettings;
use uv_resolver::PrereleaseMode;
use uv_settings::{Combine, PipOptions, ResolverInstallerOptions, ResolverOptions};
use uv_warnings::owo_colors::OwoColorize;
use crate::{
BuildOptionsArgs, FetchArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
@ -9,12 +12,27 @@ use crate::{
};
/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag.
pub fn flag(yes: bool, no: bool) -> Option<bool> {
pub fn flag(yes: bool, no: bool, name: &str) -> Option<bool> {
match (yes, no) {
(true, false) => Some(true),
(false, true) => Some(false),
(false, false) => None,
(..) => unreachable!("Clap should make this impossible"),
(..) => {
eprintln!(
"{}{} `{}` and `{}` cannot be used together. \
Boolean flags on different levels are currently not supported \
(https://github.com/clap-rs/clap/issues/6049)",
"error".bold().red(),
":".bold(),
format!("--{name}").green(),
format!("--no-{name}").green(),
);
// No error forwarding since should eventually be solved on the clap side.
#[allow(clippy::exit)]
{
std::process::exit(2);
}
}
}
}
@ -26,7 +44,7 @@ impl From<RefreshArgs> for Refresh {
refresh_package,
} = value;
Self::from_args(flag(refresh, no_refresh), refresh_package)
Self::from_args(flag(refresh, no_refresh, "no-refresh"), refresh_package)
}
}
@ -53,7 +71,7 @@ impl From<ResolverArgs> for PipOptions {
} = args;
Self {
upgrade: flag(upgrade, no_upgrade),
upgrade: flag(upgrade, no_upgrade, "no-upgrade"),
upgrade_package: Some(upgrade_package),
index_strategy,
keyring_provider,
@ -66,7 +84,7 @@ impl From<ResolverArgs> for PipOptions {
},
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
@ -96,16 +114,16 @@ impl From<InstallerArgs> for PipOptions {
} = args;
Self {
reinstall: flag(reinstall, no_reinstall),
reinstall: flag(reinstall, no_reinstall, "reinstall"),
reinstall_package: Some(reinstall_package),
index_strategy,
keyring_provider,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
exclude_newer,
link_mode,
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
no_sources: if no_sources { Some(true) } else { None },
..PipOptions::from(index_args)
}
@ -140,9 +158,9 @@ impl From<ResolverInstallerArgs> for PipOptions {
} = args;
Self {
upgrade: flag(upgrade, no_upgrade),
upgrade: flag(upgrade, no_upgrade, "upgrade"),
upgrade_package: Some(upgrade_package),
reinstall: flag(reinstall, no_reinstall),
reinstall: flag(reinstall, no_reinstall, "reinstall"),
reinstall_package: Some(reinstall_package),
index_strategy,
keyring_provider,
@ -155,11 +173,11 @@ impl From<ResolverInstallerArgs> for PipOptions {
fork_strategy,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
no_sources: if no_sources { Some(true) } else { None },
..PipOptions::from(index_args)
}
@ -289,7 +307,7 @@ pub fn resolver_options(
.filter_map(Maybe::into_option)
.collect()
}),
upgrade: flag(upgrade, no_upgrade),
upgrade: flag(upgrade, no_upgrade, "no-upgrade"),
upgrade_package: Some(upgrade_package),
index_strategy,
keyring_provider,
@ -303,13 +321,13 @@ pub fn resolver_options(
dependency_metadata: None,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
no_build_isolation_package: Some(no_build_isolation_package),
exclude_newer,
link_mode,
no_build: flag(no_build, build),
no_build: flag(no_build, build, "build"),
no_build_package: Some(no_build_package),
no_binary: flag(no_binary, binary),
no_binary: flag(no_binary, binary, "binary"),
no_binary_package: Some(no_binary_package),
no_sources: if no_sources { Some(true) } else { None },
}
@ -386,13 +404,13 @@ pub fn resolver_installer_options(
.filter_map(Maybe::into_option)
.collect()
}),
upgrade: flag(upgrade, no_upgrade),
upgrade: flag(upgrade, no_upgrade, "upgrade"),
upgrade_package: if upgrade_package.is_empty() {
None
} else {
Some(upgrade_package)
},
reinstall: flag(reinstall, no_reinstall),
reinstall: flag(reinstall, no_reinstall, "reinstall"),
reinstall_package: if reinstall_package.is_empty() {
None
} else {
@ -410,7 +428,7 @@ pub fn resolver_installer_options(
dependency_metadata: None,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
no_build_isolation_package: if no_build_isolation_package.is_empty() {
None
} else {
@ -418,14 +436,14 @@ pub fn resolver_installer_options(
},
exclude_newer,
link_mode,
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
no_build: flag(no_build, build),
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
no_build: flag(no_build, build, "build"),
no_build_package: if no_build_package.is_empty() {
None
} else {
Some(no_build_package)
},
no_binary: flag(no_binary, binary),
no_binary: flag(no_binary, binary, "binary"),
no_binary_package: if no_binary_package.is_empty() {
None
} else {

View file

@ -118,16 +118,20 @@ impl GlobalSettings {
},
show_settings: args.show_settings,
preview: PreviewMode::from(
flag(args.preview, args.no_preview)
flag(args.preview, args.no_preview, "preview")
.combine(workspace.and_then(|workspace| workspace.globals.preview))
.unwrap_or(false),
),
python_preference,
python_downloads: flag(args.allow_python_downloads, args.no_python_downloads)
.map(PythonDownloads::from)
.combine(env(env::UV_PYTHON_DOWNLOADS))
.combine(workspace.and_then(|workspace| workspace.globals.python_downloads))
.unwrap_or_default(),
python_downloads: flag(
args.allow_python_downloads,
args.no_python_downloads,
"python-downloads",
)
.map(PythonDownloads::from)
.combine(env(env::UV_PYTHON_DOWNLOADS))
.combine(workspace.and_then(|workspace| workspace.globals.python_downloads))
.unwrap_or_default(),
// Disable the progress bar with `RUST_LOG` to avoid progress fragments interleaving
// with log messages.
no_progress: args.no_progress || std::env::var_os(EnvVars::RUST_LOG).is_some(),
@ -161,7 +165,7 @@ pub(crate) struct NetworkSettings {
impl NetworkSettings {
pub(crate) fn resolve(args: &GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self {
let connectivity = if flag(args.offline, args.no_offline)
let connectivity = if flag(args.offline, args.no_offline, "offline")
.combine(workspace.and_then(|workspace| workspace.globals.offline))
.unwrap_or(false)
{
@ -169,7 +173,7 @@ impl NetworkSettings {
} else {
Connectivity::Online
};
let native_tls = flag(args.native_tls, args.no_native_tls)
let native_tls = flag(args.native_tls, args.no_native_tls, "native-tls")
.combine(workspace.and_then(|workspace| workspace.globals.native_tls))
.unwrap_or(false);
let allow_insecure_host = args
@ -274,8 +278,12 @@ impl InitSettings {
(_, _, _) => unreachable!("`app`, `lib`, and `script` are mutually exclusive"),
};
let package = flag(package || build_backend.is_some(), no_package || r#virtual)
.unwrap_or(kind.packaged_by_default());
let package = flag(
package || build_backend.is_some(),
no_package || r#virtual,
"virtual",
)
.unwrap_or(kind.packaged_by_default());
let install_mirrors = filesystem
.map(|fs| fs.install_mirrors.clone())
@ -295,7 +303,7 @@ impl InitSettings {
build_backend,
no_readme: no_readme || bare,
author_from,
pin_python: flag(pin_python, no_pin_python).unwrap_or(!bare),
pin_python: flag(pin_python, no_pin_python, "pin-python").unwrap_or(!bare),
no_workspace,
python: python.and_then(Maybe::into_option),
install_mirrors,
@ -398,7 +406,7 @@ impl RunSettings {
false,
// TODO(blueraft): support only_extra
vec![],
flag(all_extras, no_all_extras).unwrap_or_default(),
flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
),
groups: DependencyGroups::from_args(
dev,
@ -411,7 +419,7 @@ impl RunSettings {
all_groups,
),
editable: EditableMode::from_args(no_editable),
modifications: if flag(exact, inexact).unwrap_or(false) {
modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
Modifications::Exact
} else {
Modifications::Sufficient
@ -434,7 +442,7 @@ impl RunSettings {
package,
no_project,
no_sync,
active: flag(active, no_active),
active: flag(active, no_active, "active"),
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
settings: ResolverInstallerSettings::combine(
@ -1081,7 +1089,7 @@ impl PythonFindSettings {
request,
show_version,
no_project,
system: flag(system, no_system).unwrap_or_default(),
system: flag(system, no_system, "system").unwrap_or_default(),
}
}
}
@ -1116,7 +1124,7 @@ impl PythonPinSettings {
Self {
request,
resolved: flag(resolved, no_resolved).unwrap_or(false),
resolved: flag(resolved, no_resolved, "resolved").unwrap_or(false),
no_project,
global,
rm,
@ -1195,7 +1203,7 @@ impl SyncSettings {
filesystem,
);
let check = flag(check, no_check).unwrap_or_default();
let check = flag(check, no_check, "check").unwrap_or_default();
let dry_run = if check {
DryRun::Check
} else {
@ -1207,7 +1215,7 @@ impl SyncSettings {
frozen,
dry_run,
script,
active: flag(active, no_active),
active: flag(active, no_active, "active"),
extras: ExtrasSpecification::from_args(
extra.unwrap_or_default(),
no_extra,
@ -1215,7 +1223,7 @@ impl SyncSettings {
false,
// TODO(blueraft): support only_extra
vec![],
flag(all_extras, no_all_extras).unwrap_or_default(),
flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
),
groups: DependencyGroups::from_args(
dev,
@ -1233,7 +1241,7 @@ impl SyncSettings {
no_install_workspace,
no_install_package,
),
modifications: if flag(exact, inexact).unwrap_or(true) {
modifications: if flag(exact, inexact, "inexact").unwrap_or(true) {
Modifications::Exact
} else {
Modifications::Sufficient
@ -1437,7 +1445,7 @@ impl AddSettings {
Self {
locked,
frozen,
active: flag(active, no_active),
active: flag(active, no_active, "active"),
no_sync,
packages,
requirements,
@ -1455,7 +1463,7 @@ impl AddSettings {
package,
script,
python: python.and_then(Maybe::into_option),
editable: flag(editable, no_editable),
editable: flag(editable, no_editable, "editable"),
extras: extra.unwrap_or_default(),
refresh: Refresh::from(refresh),
indexes,
@ -1531,7 +1539,7 @@ impl RemoveSettings {
Self {
locked,
frozen,
active: flag(active, no_active),
active: flag(active, no_active, "active"),
no_sync,
packages,
dependency_type,
@ -1603,7 +1611,7 @@ impl VersionSettings {
dry_run,
locked,
frozen,
active: flag(active, no_active),
active: flag(active, no_active, "active"),
no_sync,
package,
python: python.and_then(Maybe::into_option),
@ -1779,7 +1787,7 @@ impl ExportSettings {
false,
// TODO(blueraft): support only_extra
vec![],
flag(all_extras, no_all_extras).unwrap_or_default(),
flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
),
groups: DependencyGroups::from_args(
dev,
@ -1792,7 +1800,7 @@ impl ExportSettings {
all_groups,
),
editable: EditableMode::from_args(no_editable),
hashes: flag(hashes, no_hashes).unwrap_or(true),
hashes: flag(hashes, no_hashes, "hashes").unwrap_or(true),
install_options: InstallOptions::new(
no_emit_project,
no_emit_workspace,
@ -1801,8 +1809,8 @@ impl ExportSettings {
output_file,
locked,
frozen,
include_annotations: flag(annotate, no_annotate).unwrap_or(true),
include_header: flag(header, no_header).unwrap_or(true),
include_annotations: flag(annotate, no_annotate, "annotate").unwrap_or(true),
include_header: flag(header, no_header, "header").unwrap_or(true),
script,
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
@ -1955,30 +1963,42 @@ impl PipCompileSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
no_build: flag(no_build, build),
system: flag(system, no_system, "system"),
no_build: flag(no_build, build, "build"),
no_binary,
only_binary,
extra,
all_extras: flag(all_extras, no_all_extras),
no_deps: flag(no_deps, deps),
all_extras: flag(all_extras, no_all_extras, "all-extras"),
no_deps: flag(no_deps, deps, "deps"),
group: Some(group),
output_file,
no_strip_extras: flag(no_strip_extras, strip_extras),
no_strip_markers: flag(no_strip_markers, strip_markers),
no_annotate: flag(no_annotate, annotate),
no_header: flag(no_header, header),
no_strip_extras: flag(no_strip_extras, strip_extras, "strip-extras"),
no_strip_markers: flag(no_strip_markers, strip_markers, "strip-markers"),
no_annotate: flag(no_annotate, annotate, "annotate"),
no_header: flag(no_header, header, "header"),
custom_compile_command,
generate_hashes: flag(generate_hashes, no_generate_hashes),
generate_hashes: flag(generate_hashes, no_generate_hashes, "generate-hashes"),
python_version,
python_platform,
universal: flag(universal, no_universal),
universal: flag(universal, no_universal, "universal"),
no_emit_package,
emit_index_url: flag(emit_index_url, no_emit_index_url),
emit_find_links: flag(emit_find_links, no_emit_find_links),
emit_build_options: flag(emit_build_options, no_emit_build_options),
emit_marker_expression: flag(emit_marker_expression, no_emit_marker_expression),
emit_index_annotation: flag(emit_index_annotation, no_emit_index_annotation),
emit_index_url: flag(emit_index_url, no_emit_index_url, "emit-index-url"),
emit_find_links: flag(emit_find_links, no_emit_find_links, "emit-find-links"),
emit_build_options: flag(
emit_build_options,
no_emit_build_options,
"emit-build-options",
),
emit_marker_expression: flag(
emit_marker_expression,
no_emit_marker_expression,
"emit-marker-expression",
),
emit_index_annotation: flag(
emit_index_annotation,
no_emit_index_annotation,
"emit-index-annotation",
),
annotation_style,
torch_backend,
..PipOptions::from(resolver)
@ -2050,22 +2070,27 @@ impl PipSyncSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
break_system_packages: flag(break_system_packages, no_break_system_packages),
system: flag(system, no_system, "system"),
break_system_packages: flag(
break_system_packages,
no_break_system_packages,
"break-system-packages",
),
target,
prefix,
require_hashes: flag(require_hashes, no_require_hashes),
verify_hashes: flag(verify_hashes, no_verify_hashes),
no_build: flag(no_build, build),
require_hashes: flag(require_hashes, no_require_hashes, "require-hashes"),
verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes"),
no_build: flag(no_build, build, "build"),
no_binary,
only_binary,
allow_empty_requirements: flag(
allow_empty_requirements,
no_allow_empty_requirements,
"allow-empty-requirements",
),
python_version,
python_platform,
strict: flag(strict, no_strict),
strict: flag(strict, no_strict, "strict"),
torch_backend,
..PipOptions::from(installer)
},
@ -2199,7 +2224,7 @@ impl PipInstallSettings {
constraints_from_workspace,
overrides_from_workspace,
build_constraints_from_workspace,
modifications: if flag(exact, inexact).unwrap_or(false) {
modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
Modifications::Exact
} else {
Modifications::Sufficient
@ -2208,22 +2233,26 @@ impl PipInstallSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
break_system_packages: flag(break_system_packages, no_break_system_packages),
system: flag(system, no_system, "system"),
break_system_packages: flag(
break_system_packages,
no_break_system_packages,
"break-system-packages",
),
target,
prefix,
no_build: flag(no_build, build),
no_build: flag(no_build, build, "build"),
no_binary,
only_binary,
strict: flag(strict, no_strict),
strict: flag(strict, no_strict, "strict"),
extra,
all_extras: flag(all_extras, no_all_extras),
all_extras: flag(all_extras, no_all_extras, "all-extras"),
group: Some(group),
no_deps: flag(no_deps, deps),
no_deps: flag(no_deps, deps, "deps"),
python_version,
python_platform,
require_hashes: flag(require_hashes, no_require_hashes),
verify_hashes: flag(verify_hashes, no_verify_hashes),
require_hashes: flag(require_hashes, no_require_hashes, "require-hashes"),
verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes"),
torch_backend,
..PipOptions::from(installer)
},
@ -2267,8 +2296,12 @@ impl PipUninstallSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
break_system_packages: flag(break_system_packages, no_break_system_packages),
system: flag(system, no_system, "system"),
break_system_packages: flag(
break_system_packages,
no_break_system_packages,
"break-system-packages",
),
target,
prefix,
keyring_provider,
@ -2308,8 +2341,8 @@ impl PipFreezeSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
system: flag(system, no_system, "system"),
strict: flag(strict, no_strict, "strict"),
..PipOptions::default()
},
filesystem,
@ -2348,15 +2381,15 @@ impl PipListSettings {
} = args;
Self {
editable: flag(editable, exclude_editable),
editable: flag(editable, exclude_editable, "exclude-editable"),
exclude,
format,
outdated: flag(outdated, no_outdated).unwrap_or(false),
outdated: flag(outdated, no_outdated, "outdated").unwrap_or(false),
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
system: flag(system, no_system, "system"),
strict: flag(strict, no_strict, "strict"),
..PipOptions::from(fetch)
},
filesystem,
@ -2393,8 +2426,8 @@ impl PipShowSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
system: flag(system, no_system, "system"),
strict: flag(strict, no_strict, "strict"),
..PipOptions::default()
},
filesystem,
@ -2442,8 +2475,8 @@ impl PipTreeSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
strict: flag(strict, no_strict),
system: flag(system, no_system, "system"),
strict: flag(strict, no_strict, "strict"),
..PipOptions::from(fetch)
},
filesystem,
@ -2471,7 +2504,7 @@ impl PipCheckSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
system: flag(system, no_system, "system"),
..PipOptions::default()
},
filesystem,
@ -2538,15 +2571,15 @@ impl BuildSettings {
sdist,
wheel,
list,
build_logs: flag(build_logs, no_build_logs).unwrap_or(true),
build_logs: flag(build_logs, no_build_logs, "build-logs").unwrap_or(true),
build_constraints: build_constraints
.into_iter()
.filter_map(Maybe::into_option)
.collect(),
force_pep517,
hash_checking: HashCheckingMode::from_args(
flag(require_hashes, no_require_hashes),
flag(verify_hashes, no_verify_hashes),
flag(require_hashes, no_require_hashes, "require-hashes"),
flag(verify_hashes, no_verify_hashes, "verify-hashes"),
),
python: python.and_then(Maybe::into_option),
refresh: Refresh::from(refresh),
@ -2605,7 +2638,7 @@ impl VenvSettings {
settings: PipSettings::combine(
PipOptions {
python: python.and_then(Maybe::into_option),
system: flag(system, no_system),
system: flag(system, no_system, "system"),
index_strategy,
keyring_provider,
exclude_newer,

View file

@ -11486,3 +11486,25 @@ fn pep_751_dependency() -> Result<()> {
Ok(())
}
/// Test that we show an error instead of panicking for conflicting arguments in different levels,
/// which are not caught by clap.
#[test]
fn conflicting_flags_clap_bug() {
let context = TestContext::new("3.12");
uv_snapshot!(context.filters(), context.command()
.arg("pip")
.arg("--offline")
.arg("install")
.arg("--no-offline")
.arg("tqdm"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: `--offline` and `--no-offline` cannot be used together. Boolean flags on different levels are currently not supported (https://github.com/clap-rs/clap/issues/6049)
"
);
}