Initial vendor packages

Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
2024-01-08 01:21:28 +04:00
parent 5ecd8cf2cb
commit 1b6a04ca55
7309 changed files with 2160054 additions and 0 deletions

File diff suppressed because one or more lines are too long

139
vendor/clap_builder/Cargo.toml vendored Normal file
View File

@ -0,0 +1,139 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.70.0"
name = "clap_builder"
version = "4.4.12"
include = [
"build.rs",
"src/**/*",
"Cargo.toml",
"LICENSE*",
"README.md",
"benches/**/*",
"examples/**/*",
]
description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
readme = "README.md"
keywords = [
"argument",
"cli",
"arg",
"parser",
"parse",
]
categories = ["command-line-interface"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/clap-rs/clap"
[package.metadata.docs.rs]
cargo-args = [
"-Zunstable-options",
"-Zrustdoc-scrape-examples",
]
features = ["unstable-doc"]
rustdoc-args = [
"--cfg",
"docsrs",
]
[package.metadata.playground]
features = ["unstable-doc"]
[package.metadata.release]
dependent-version = "upgrade"
shared-version = true
tag-name = "v{{version}}"
[lib]
bench = false
[dependencies.anstream]
version = "0.6.0"
optional = true
[dependencies.anstyle]
version = "1.0.0"
[dependencies.backtrace]
version = "0.3.67"
optional = true
[dependencies.clap_lex]
version = "0.6.0"
[dependencies.strsim]
version = "0.10.0"
optional = true
[dependencies.terminal_size]
version = "0.3.0"
optional = true
[dependencies.unicase]
version = "2.6.0"
optional = true
[dependencies.unicode-width]
version = "0.1.9"
optional = true
[dev-dependencies.color-print]
version = "0.3.5"
[dev-dependencies.static_assertions]
version = "1.1.0"
[dev-dependencies.unic-emoji-char]
version = "0.9.0"
[features]
cargo = []
color = ["dep:anstream"]
debug = ["dep:backtrace"]
default = [
"std",
"color",
"help",
"usage",
"error-context",
"suggestions",
]
deprecated = []
env = []
error-context = []
help = []
std = ["anstyle/std"]
string = []
suggestions = [
"dep:strsim",
"error-context",
]
unicode = [
"dep:unicode-width",
"dep:unicase",
]
unstable-doc = [
"cargo",
"wrap_help",
"env",
"unicode",
"string",
]
unstable-styles = ["color"]
unstable-v5 = ["deprecated"]
usage = []
wrap_help = [
"help",
"dep:terminal_size",
]

201
vendor/clap_builder/LICENSE-APACHE vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

21
vendor/clap_builder/LICENSE-MIT vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2022 Kevin B. Knapp and Clap Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

24
vendor/clap_builder/README.md vendored Normal file
View File

@ -0,0 +1,24 @@
# clap_builder
Builder implementation for clap.
[docs.rs](https://docs.rs/clap)
- [Derive Tutorial](https://docs.rs/clap/latest/clap/_derive/_tutorial/index.html)
- [Derive Reference](https://docs.rs/clap/latest/clap/_derive/index.html)
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
See [CONTRIBUTING](CONTRIBUTING.md) for more details.

View File

@ -0,0 +1,418 @@
#[cfg(debug_assertions)]
use crate::util::AnyValueId;
/// Behavior of arguments when they are encountered while parsing
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "help")] {
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("special-help")
/// .short('?')
/// .action(clap::ArgAction::Help)
/// );
///
/// // Existing help still exists
/// let err = cmd.clone().try_get_matches_from(["mycmd", "-h"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
///
/// // New help available
/// let err = cmd.try_get_matches_from(["mycmd", "-?"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
/// # }
/// ```
#[derive(Clone, Debug)]
#[non_exhaustive]
#[allow(missing_copy_implementations)] // In the future, we may accept `Box<dyn ...>`
pub enum ArgAction {
/// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches]
///
/// **NOTE:** If the argument has previously been seen, it will result in a
/// [`ArgumentConflict`][crate::error::ErrorKind::ArgumentConflict] unless
/// [`Command::args_override_self(true)`][crate::Command::args_override_self] is set.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("flag")
/// .long("flag")
/// .action(clap::ArgAction::Set)
/// );
///
/// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_many::<String>("flag").unwrap_or_default().map(|v| v.as_str()).collect::<Vec<_>>(),
/// vec!["value"]
/// );
/// ```
Set,
/// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches]
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("flag")
/// .long("flag")
/// .action(clap::ArgAction::Append)
/// );
///
/// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value1", "--flag", "value2"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_many::<String>("flag").unwrap_or_default().map(|v| v.as_str()).collect::<Vec<_>>(),
/// vec!["value1", "value2"]
/// );
/// ```
Append,
/// When encountered, act as if `"true"` was encountered on the command-line
///
/// If no [`default_value`][super::Arg::default_value] is set, it will be `false`.
///
/// No value is allowed. To optionally accept a value, see
/// [`Arg::default_missing_value`][super::Arg::default_missing_value]
///
/// **NOTE:** If the argument has previously been seen, it will result in a
/// [`ArgumentConflict`][crate::error::ErrorKind::ArgumentConflict] unless
/// [`Command::args_override_self(true)`][crate::Command::args_override_self] is set.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("flag")
/// .long("flag")
/// .action(clap::ArgAction::SetTrue)
/// );
///
/// let matches = cmd.clone().try_get_matches_from(["mycmd", "--flag"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_flag("flag"),
/// true
/// );
///
/// let matches = cmd.try_get_matches_from(["mycmd"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_flag("flag"),
/// false
/// );
/// ```
///
/// You can use [`TypedValueParser::map`][crate::builder::TypedValueParser::map] to have the
/// flag control an application-specific type:
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// # use clap::builder::TypedValueParser as _;
/// # use clap::builder::BoolishValueParser;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("flag")
/// .long("flag")
/// .action(clap::ArgAction::SetTrue)
/// .value_parser(
/// BoolishValueParser::new()
/// .map(|b| -> usize {
/// if b { 10 } else { 5 }
/// })
/// )
/// );
///
/// let matches = cmd.clone().try_get_matches_from(["mycmd", "--flag"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_one::<usize>("flag").copied(),
/// Some(10)
/// );
///
/// let matches = cmd.try_get_matches_from(["mycmd"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_one::<usize>("flag").copied(),
/// Some(5)
/// );
/// ```
SetTrue,
/// When encountered, act as if `"false"` was encountered on the command-line
///
/// If no [`default_value`][super::Arg::default_value] is set, it will be `true`.
///
/// No value is allowed. To optionally accept a value, see
/// [`Arg::default_missing_value`][super::Arg::default_missing_value]
///
/// **NOTE:** If the argument has previously been seen, it will result in a
/// [`ArgumentConflict`][crate::error::ErrorKind::ArgumentConflict] unless
/// [`Command::args_override_self(true)`][crate::Command::args_override_self] is set.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("flag")
/// .long("flag")
/// .action(clap::ArgAction::SetFalse)
/// );
///
/// let matches = cmd.clone().try_get_matches_from(["mycmd", "--flag"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_flag("flag"),
/// false
/// );
///
/// let matches = cmd.try_get_matches_from(["mycmd"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_flag("flag"),
/// true
/// );
/// ```
SetFalse,
/// When encountered, increment a `u8` counter
///
/// If no [`default_value`][super::Arg::default_value] is set, it will be `0`.
///
/// No value is allowed. To optionally accept a value, see
/// [`Arg::default_missing_value`][super::Arg::default_missing_value]
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("flag")
/// .long("flag")
/// .action(clap::ArgAction::Count)
/// );
///
/// let matches = cmd.clone().try_get_matches_from(["mycmd", "--flag", "--flag"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_count("flag"),
/// 2
/// );
///
/// let matches = cmd.try_get_matches_from(["mycmd"]).unwrap();
/// assert!(matches.contains_id("flag"));
/// assert_eq!(
/// matches.get_count("flag"),
/// 0
/// );
/// ```
Count,
/// When encountered, display [`Command::print_help`][super::Command::print_help]
///
/// Depending on the flag, [`Command::print_long_help`][super::Command::print_long_help] may be shown
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "help")] {
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("special-help")
/// .short('?')
/// .action(clap::ArgAction::Help)
/// );
///
/// // Existing help still exists
/// let err = cmd.clone().try_get_matches_from(["mycmd", "-h"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
///
/// // New help available
/// let err = cmd.try_get_matches_from(["mycmd", "-?"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
/// # }
/// ```
Help,
/// When encountered, display [`Command::print_help`][super::Command::print_help]
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "help")] {
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("special-help")
/// .short('?')
/// .action(clap::ArgAction::HelpShort)
/// );
///
/// // Existing help still exists
/// let err = cmd.clone().try_get_matches_from(["mycmd", "-h"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
///
/// // New help available
/// let err = cmd.try_get_matches_from(["mycmd", "-?"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
/// # }
/// ```
HelpShort,
/// When encountered, display [`Command::print_long_help`][super::Command::print_long_help]
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "help")] {
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .arg(
/// Arg::new("special-help")
/// .short('?')
/// .action(clap::ArgAction::HelpLong)
/// );
///
/// // Existing help still exists
/// let err = cmd.clone().try_get_matches_from(["mycmd", "-h"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
///
/// // New help available
/// let err = cmd.try_get_matches_from(["mycmd", "-?"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp);
/// # }
/// ```
HelpLong,
/// When encountered, display [`Command::version`][super::Command::version]
///
/// Depending on the flag, [`Command::long_version`][super::Command::long_version] may be shown
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// let cmd = Command::new("mycmd")
/// .version("1.0.0")
/// .arg(
/// Arg::new("special-version")
/// .long("special-version")
/// .action(clap::ArgAction::Version)
/// );
///
/// // Existing help still exists
/// let err = cmd.clone().try_get_matches_from(["mycmd", "--version"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayVersion);
///
/// // New help available
/// let err = cmd.try_get_matches_from(["mycmd", "--special-version"]).unwrap_err();
/// assert_eq!(err.kind(), clap::error::ErrorKind::DisplayVersion);
/// ```
Version,
}
impl ArgAction {
/// Returns whether this action accepts values on the command-line
///
/// [`default_values`][super::Arg::default_values] and [`env`][super::Arg::env] may still be
/// processed.
pub fn takes_values(&self) -> bool {
match self {
Self::Set => true,
Self::Append => true,
Self::SetTrue => false,
Self::SetFalse => false,
Self::Count => false,
Self::Help => false,
Self::HelpShort => false,
Self::HelpLong => false,
Self::Version => false,
}
}
pub(crate) fn default_value(&self) -> Option<&'static std::ffi::OsStr> {
match self {
Self::Set => None,
Self::Append => None,
Self::SetTrue => Some(std::ffi::OsStr::new("false")),
Self::SetFalse => Some(std::ffi::OsStr::new("true")),
Self::Count => Some(std::ffi::OsStr::new("0")),
Self::Help => None,
Self::HelpShort => None,
Self::HelpLong => None,
Self::Version => None,
}
}
pub(crate) fn default_missing_value(&self) -> Option<&'static std::ffi::OsStr> {
match self {
Self::Set => None,
Self::Append => None,
Self::SetTrue => Some(std::ffi::OsStr::new("true")),
Self::SetFalse => Some(std::ffi::OsStr::new("false")),
Self::Count => None,
Self::Help => None,
Self::HelpShort => None,
Self::HelpLong => None,
Self::Version => None,
}
}
pub(crate) fn default_value_parser(&self) -> Option<super::ValueParser> {
match self {
Self::Set => None,
Self::Append => None,
Self::SetTrue => Some(super::ValueParser::bool()),
Self::SetFalse => Some(super::ValueParser::bool()),
Self::Count => Some(crate::value_parser!(u8).into()),
Self::Help => None,
Self::HelpShort => None,
Self::HelpLong => None,
Self::Version => None,
}
}
#[cfg(debug_assertions)]
pub(crate) fn value_type_id(&self) -> Option<AnyValueId> {
match self {
Self::Set => None,
Self::Append => None,
Self::SetTrue => None,
Self::SetFalse => None,
Self::Count => Some(AnyValueId::of::<CountType>()),
Self::Help => None,
Self::HelpShort => None,
Self::HelpLong => None,
Self::Version => None,
}
}
}
pub(crate) type CountType = u8;

View File

@ -0,0 +1,84 @@
#[allow(unused)]
use crate::Arg;
#[allow(unused)]
use crate::Command;
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct AppFlags(u32);
impl AppFlags {
pub(crate) fn set(&mut self, setting: AppSettings) {
self.0 |= setting.bit();
}
pub(crate) fn unset(&mut self, setting: AppSettings) {
self.0 &= !setting.bit();
}
pub(crate) fn is_set(&self, setting: AppSettings) -> bool {
self.0 & setting.bit() != 0
}
pub(crate) fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
}
impl std::ops::BitOr for AppFlags {
type Output = Self;
fn bitor(mut self, rhs: Self) -> Self::Output {
self.insert(rhs);
self
}
}
/// Application level settings, which affect how [`Command`] operates
///
/// **NOTE:** When these settings are used, they apply only to current command, and are *not*
/// propagated down or up through child or parent subcommands
///
/// [`Command`]: crate::Command
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub(crate) enum AppSettings {
IgnoreErrors,
AllowHyphenValues,
AllowNegativeNumbers,
AllArgsOverrideSelf,
AllowMissingPositional,
TrailingVarArg,
DontDelimitTrailingValues,
InferLongArgs,
InferSubcommands,
SubcommandRequired,
AllowExternalSubcommands,
Multicall,
SubcommandsNegateReqs,
ArgsNegateSubcommands,
SubcommandPrecedenceOverArg,
FlattenHelp,
ArgRequiredElseHelp,
NextLineHelp,
DisableColoredHelp,
DisableHelpFlag,
DisableHelpSubcommand,
DisableVersionFlag,
PropagateVersion,
Hidden,
HidePossibleValues,
HelpExpected,
NoBinaryName,
#[allow(dead_code)]
ColorAuto,
ColorAlways,
ColorNever,
Built,
BinNameBuilt,
}
impl AppSettings {
fn bit(self) -> u32 {
1 << (self as u8)
}
}

4800
vendor/clap_builder/src/builder/arg.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,611 @@
// Internal
use crate::builder::IntoResettable;
use crate::util::Id;
/// Family of related [arguments].
///
/// By placing arguments in a logical group, you can create easier requirement and
/// exclusion rules instead of having to list each argument individually, or when you want a rule
/// to apply "any but not all" arguments.
///
/// For instance, you can make an entire `ArgGroup` required. If [`ArgGroup::multiple(true)`] is
/// set, this means that at least one argument from that group must be present. If
/// [`ArgGroup::multiple(false)`] is set (the default), one and *only* one must be present.
///
/// You can also do things such as name an entire `ArgGroup` as a [conflict] or [requirement] for
/// another argument, meaning any of the arguments that belong to that group will cause a failure
/// if present, or must be present respectively.
///
/// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be
/// present out of a given set. Imagine that you had multiple arguments, and you want one of them
/// to be required, but making all of them required isn't feasible because perhaps they conflict
/// with each other. For example, lets say that you were building an application where one could
/// set a given version number by supplying a string with an option argument, i.e.
/// `--set-ver v1.2.3`, you also wanted to support automatically using a previous version number
/// and simply incrementing one of the three numbers. So you create three flags `--major`,
/// `--minor`, and `--patch`. All of these arguments shouldn't be used at one time but you want to
/// specify that *at least one* of them is used. For this, you can create a group.
///
/// Finally, you may use `ArgGroup`s to pull a value from a group of arguments when you don't care
/// exactly which argument was actually used at runtime.
///
/// # Examples
///
/// The following example demonstrates using an `ArgGroup` to ensure that one, and only one, of
/// the arguments from the specified group is present at runtime.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, arg, ArgGroup, error::ErrorKind};
/// let result = Command::new("cmd")
/// .arg(arg!(--"set-ver" <ver> "set the version manually"))
/// .arg(arg!(--major "auto increase major"))
/// .arg(arg!(--minor "auto increase minor"))
/// .arg(arg!(--patch "auto increase patch"))
/// .group(ArgGroup::new("vers")
/// .args(["set-ver", "major", "minor", "patch"])
/// .required(true))
/// .try_get_matches_from(vec!["cmd", "--major", "--patch"]);
/// // Because we used two args in the group it's an error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
/// ```
///
/// This next example shows a passing parse of the same scenario
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, arg, ArgGroup, Id};
/// let result = Command::new("cmd")
/// .arg(arg!(--"set-ver" <ver> "set the version manually"))
/// .arg(arg!(--major "auto increase major"))
/// .arg(arg!(--minor "auto increase minor"))
/// .arg(arg!(--patch "auto increase patch"))
/// .group(ArgGroup::new("vers")
/// .args(["set-ver", "major", "minor","patch"])
/// .required(true))
/// .try_get_matches_from(vec!["cmd", "--major"]);
/// assert!(result.is_ok());
/// let matches = result.unwrap();
/// // We may not know which of the args was used, so we can test for the group...
/// assert!(matches.contains_id("vers"));
/// // We can also ask the group which arg was used
/// assert_eq!(matches
/// .get_one::<Id>("vers")
/// .expect("`vers` is required")
/// .as_str(),
/// "major"
/// );
/// // we could also alternatively check each arg individually (not shown here)
/// ```
/// [`ArgGroup::multiple(true)`]: ArgGroup::multiple()
///
/// [`ArgGroup::multiple(false)`]: ArgGroup::multiple()
/// [arguments]: crate::Arg
/// [conflict]: crate::Arg::conflicts_with()
/// [requirement]: crate::Arg::requires()
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct ArgGroup {
pub(crate) id: Id,
pub(crate) args: Vec<Id>,
pub(crate) required: bool,
pub(crate) requires: Vec<Id>,
pub(crate) conflicts: Vec<Id>,
pub(crate) multiple: bool,
}
/// # Builder
impl ArgGroup {
/// Create a `ArgGroup` using a unique name.
///
/// The name will be used to get values from the group or refer to the group inside of conflict
/// and requirement rules.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, ArgGroup};
/// ArgGroup::new("config")
/// # ;
/// ```
pub fn new(id: impl Into<Id>) -> Self {
ArgGroup::default().id(id)
}
/// Sets the group name.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, ArgGroup};
/// ArgGroup::default().id("config")
/// # ;
/// ```
#[must_use]
pub fn id(mut self, id: impl Into<Id>) -> Self {
self.id = id.into();
self
}
/// Adds an [argument] to this group by name
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, ArgAction};
/// let m = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .arg("flag")
/// .arg("color"))
/// .get_matches_from(vec!["myprog", "-f"]);
/// // maybe we don't know which of the two flags was used...
/// assert!(m.contains_id("req_flags"));
/// // but we can also check individually if needed
/// assert!(m.contains_id("flag"));
/// ```
/// [argument]: crate::Arg
#[must_use]
pub fn arg(mut self, arg_id: impl IntoResettable<Id>) -> Self {
if let Some(arg_id) = arg_id.into_resettable().into_option() {
self.args.push(arg_id);
} else {
self.args.clear();
}
self
}
/// Adds multiple [arguments] to this group by name
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, ArgAction};
/// let m = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"]))
/// .get_matches_from(vec!["myprog", "-f"]);
/// // maybe we don't know which of the two flags was used...
/// assert!(m.contains_id("req_flags"));
/// // but we can also check individually if needed
/// assert!(m.contains_id("flag"));
/// ```
/// [arguments]: crate::Arg
#[must_use]
pub fn args(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self {
for n in ns {
self = self.arg(n);
}
self
}
/// Getters for all args. It will return a vector of `Id`
///
/// # Example
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{ArgGroup};
/// let args: Vec<&str> = vec!["a1".into(), "a4".into()];
/// let grp = ArgGroup::new("program").args(&args);
///
/// for (pos, arg) in grp.get_args().enumerate() {
/// assert_eq!(*arg, args[pos]);
/// }
/// ```
pub fn get_args(&self) -> impl Iterator<Item = &Id> {
self.args.iter()
}
/// Allows more than one of the [`Arg`]s in this group to be used. (Default: `false`)
///
/// # Examples
///
/// Notice in this example we use *both* the `-f` and `-c` flags which are both part of the
/// group
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, ArgAction};
/// let m = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"])
/// .multiple(true))
/// .get_matches_from(vec!["myprog", "-f", "-c"]);
/// // maybe we don't know which of the two flags was used...
/// assert!(m.contains_id("req_flags"));
/// ```
/// In this next example, we show the default behavior (i.e. `multiple(false)) which will throw
/// an error if more than one of the args in the group was used.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
/// let result = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"]))
/// .try_get_matches_from(vec!["myprog", "-f", "-c"]);
/// // Because we used both args in the group it's an error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
/// ```
///
/// [`Arg`]: crate::Arg
#[inline]
#[must_use]
pub fn multiple(mut self, yes: bool) -> Self {
self.multiple = yes;
self
}
/// Return true if the group allows more than one of the arguments
/// in this group to be used. (Default: `false`)
///
/// # Example
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{ArgGroup};
/// let mut group = ArgGroup::new("myprog")
/// .args(["f", "c"])
/// .multiple(true);
///
/// assert!(group.is_multiple());
/// ```
pub fn is_multiple(&mut self) -> bool {
self.multiple
}
/// Require an argument from the group to be present when parsing.
///
/// This is unless conflicting with another argument. A required group will be displayed in
/// the usage string of the application in the format `<arg|arg2|arg3>`.
///
/// **NOTE:** This setting only applies to the current [`Command`] / [`Subcommand`]s, and not
/// globally.
///
/// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with
/// `ArgGroup::required(true)` states, "One and *only one* arg must be used from this group.
/// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which
/// states, '*At least* one arg from this group must be used. Using multiple is OK."
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
/// let result = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"])
/// .required(true))
/// .try_get_matches_from(vec!["myprog"]);
/// // Because we didn't use any of the args in the group, it's an error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
/// ```
///
/// [`Subcommand`]: crate::Subcommand
/// [`ArgGroup::multiple`]: ArgGroup::multiple()
/// [`Command`]: crate::Command
#[inline]
#[must_use]
pub fn required(mut self, yes: bool) -> Self {
self.required = yes;
self
}
/// Specify an argument or group that must be present when this group is.
///
/// This is not to be confused with a [required group]. Requirement rules function just like
/// [argument requirement rules], you can name other arguments or groups that must be present
/// when any one of the arguments from this group is used.
///
/// **NOTE:** The name provided may be an argument or group name
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
/// let result = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("debug")
/// .short('d')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"])
/// .requires("debug"))
/// .try_get_matches_from(vec!["myprog", "-c"]);
/// // because we used an arg from the group, and the group requires "-d" to be used, it's an
/// // error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
/// ```
/// [required group]: ArgGroup::required()
/// [argument requirement rules]: crate::Arg::requires()
#[must_use]
pub fn requires(mut self, id: impl IntoResettable<Id>) -> Self {
if let Some(id) = id.into_resettable().into_option() {
self.requires.push(id);
} else {
self.requires.clear();
}
self
}
/// Specify arguments or groups that must be present when this group is.
///
/// This is not to be confused with a [required group]. Requirement rules function just like
/// [argument requirement rules], you can name other arguments or groups that must be present
/// when one of the arguments from this group is used.
///
/// **NOTE:** The names provided may be an argument or group name
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
/// let result = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("debug")
/// .short('d')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("verb")
/// .short('v')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"])
/// .requires_all(["debug", "verb"]))
/// .try_get_matches_from(vec!["myprog", "-c", "-d"]);
/// // because we used an arg from the group, and the group requires "-d" and "-v" to be used,
/// // yet we only used "-d" it's an error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
/// ```
/// [required group]: ArgGroup::required()
/// [argument requirement rules]: crate::Arg::requires_ifs()
#[must_use]
pub fn requires_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self {
for n in ns {
self = self.requires(n);
}
self
}
/// Specify an argument or group that must **not** be present when this group is.
///
/// Exclusion (aka conflict) rules function just like [argument exclusion rules], you can name
/// other arguments or groups that must *not* be present when one of the arguments from this
/// group are used.
///
/// **NOTE:** The name provided may be an argument, or group name
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
/// let result = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("debug")
/// .short('d')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"])
/// .conflicts_with("debug"))
/// .try_get_matches_from(vec!["myprog", "-c", "-d"]);
/// // because we used an arg from the group, and the group conflicts with "-d", it's an error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
/// ```
/// [argument exclusion rules]: crate::Arg::conflicts_with()
#[must_use]
pub fn conflicts_with(mut self, id: impl IntoResettable<Id>) -> Self {
if let Some(id) = id.into_resettable().into_option() {
self.conflicts.push(id);
} else {
self.conflicts.clear();
}
self
}
/// Specify arguments or groups that must **not** be present when this group is.
///
/// Exclusion rules function just like [argument exclusion rules], you can name other arguments
/// or groups that must *not* be present when one of the arguments from this group are used.
///
/// **NOTE:** The names provided may be an argument, or group name
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgGroup, error::ErrorKind, ArgAction};
/// let result = Command::new("myprog")
/// .arg(Arg::new("flag")
/// .short('f')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("color")
/// .short('c')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("debug")
/// .short('d')
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("verb")
/// .short('v')
/// .action(ArgAction::SetTrue))
/// .group(ArgGroup::new("req_flags")
/// .args(["flag", "color"])
/// .conflicts_with_all(["debug", "verb"]))
/// .try_get_matches_from(vec!["myprog", "-c", "-v"]);
/// // because we used an arg from the group, and the group conflicts with either "-v" or "-d"
/// // it's an error
/// assert!(result.is_err());
/// let err = result.unwrap_err();
/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
/// ```
///
/// [argument exclusion rules]: crate::Arg::conflicts_with_all()
#[must_use]
pub fn conflicts_with_all(mut self, ns: impl IntoIterator<Item = impl Into<Id>>) -> Self {
for n in ns {
self = self.conflicts_with(n);
}
self
}
}
/// # Reflection
impl ArgGroup {
/// Get the name of the group
#[inline]
pub fn get_id(&self) -> &Id {
&self.id
}
/// Reports whether [`ArgGroup::required`] is set
#[inline]
pub fn is_required_set(&self) -> bool {
self.required
}
}
impl From<&'_ ArgGroup> for ArgGroup {
fn from(g: &ArgGroup) -> Self {
g.clone()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn groups() {
let g = ArgGroup::new("test")
.arg("a1")
.arg("a4")
.args(["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(["r2", "r3"])
.requires("r4");
let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
assert_eq!(g.args, args);
assert_eq!(g.requires, reqs);
assert_eq!(g.conflicts, confs);
}
#[test]
fn test_from() {
let g = ArgGroup::new("test")
.arg("a1")
.arg("a4")
.args(["a2", "a3"])
.required(true)
.conflicts_with("c1")
.conflicts_with_all(["c2", "c3"])
.conflicts_with("c4")
.requires("r1")
.requires_all(["r2", "r3"])
.requires("r4");
let args: Vec<Id> = vec!["a1".into(), "a4".into(), "a2".into(), "a3".into()];
let reqs: Vec<Id> = vec!["r1".into(), "r2".into(), "r3".into(), "r4".into()];
let confs: Vec<Id> = vec!["c1".into(), "c2".into(), "c3".into(), "c4".into()];
let g2 = ArgGroup::from(&g);
assert_eq!(g2.args, args);
assert_eq!(g2.requires, reqs);
assert_eq!(g2.conflicts, confs);
}
// This test will *fail to compile* if ArgGroup is not Send + Sync
#[test]
fn arg_group_send_sync() {
fn foo<T: Send + Sync>(_: T) {}
foo(ArgGroup::new("test"))
}
#[test]
fn arg_group_expose_is_multiple_helper() {
let args: Vec<Id> = vec!["a1".into(), "a4".into()];
let mut grp_multiple = ArgGroup::new("test_multiple").args(&args).multiple(true);
assert!(grp_multiple.is_multiple());
let mut grp_not_multiple = ArgGroup::new("test_multiple").args(&args).multiple(false);
assert!(!grp_not_multiple.is_multiple());
}
#[test]
fn arg_group_expose_get_args_helper() {
let args: Vec<Id> = vec!["a1".into(), "a4".into()];
let grp = ArgGroup::new("program").args(&args);
for (pos, arg) in grp.get_args().enumerate() {
assert_eq!(*arg, args[pos]);
}
}
}

View File

@ -0,0 +1,19 @@
use crate::builder::OsStr;
/// Operations to perform on argument values
///
/// These do not apply to [`ValueSource::DefaultValue`][crate::parser::ValueSource::DefaultValue]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "unstable-v5", non_exhaustive)]
pub enum ArgPredicate {
/// Is the argument present?
IsPresent,
/// Does the argument match the specified value?
Equals(OsStr),
}
impl<S: Into<OsStr>> From<S> for ArgPredicate {
fn from(other: S) -> Self {
Self::Equals(other.into())
}
}

View File

@ -0,0 +1,91 @@
#[allow(unused)]
use crate::Arg;
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct ArgFlags(u32);
impl ArgFlags {
pub(crate) fn set(&mut self, setting: ArgSettings) {
self.0 |= setting.bit();
}
pub(crate) fn unset(&mut self, setting: ArgSettings) {
self.0 &= !setting.bit();
}
pub(crate) fn is_set(&self, setting: ArgSettings) -> bool {
self.0 & setting.bit() != 0
}
pub(crate) fn insert(&mut self, other: Self) {
self.0 |= other.0;
}
}
impl std::ops::BitOr for ArgFlags {
type Output = Self;
fn bitor(mut self, rhs: Self) -> Self::Output {
self.insert(rhs);
self
}
}
/// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
/// methods [`Arg::setting`], [`Arg::unset_setting`], and [`Arg::is_set`]. This is what the
/// [`Arg`] methods which accept a `bool` use internally.
///
/// [`Arg`]: crate::Arg
/// [`Arg::setting`]: crate::Arg::setting()
/// [`Arg::unset_setting`]: crate::Arg::unset_setting()
/// [`Arg::is_set`]: crate::Arg::is_set()
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub(crate) enum ArgSettings {
Required,
Global,
Hidden,
NextLineHelp,
HidePossibleValues,
AllowHyphenValues,
AllowNegativeNumbers,
RequireEquals,
Last,
TrailingVarArg,
HideDefaultValue,
IgnoreCase,
#[cfg(feature = "env")]
HideEnv,
#[cfg(feature = "env")]
HideEnvValues,
HiddenShortHelp,
HiddenLongHelp,
Exclusive,
}
impl ArgSettings {
fn bit(self) -> u32 {
1 << (self as u8)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::Arg;
#[test]
fn setting() {
let m = Arg::new("setting").setting(ArgSettings::Required);
assert!(m.is_required_set());
}
#[test]
fn unset_setting() {
let m = Arg::new("unset_setting").setting(ArgSettings::Required);
assert!(m.is_required_set());
let m = m.unset_setting(ArgSettings::Required);
assert!(!m.is_required_set(), "{m:#?}");
}
}

4916
vendor/clap_builder/src/builder/command.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,840 @@
use std::cmp::Ordering;
use crate::builder::ValueRange;
use crate::mkeymap::KeyType;
use crate::util::FlatSet;
use crate::util::Id;
use crate::ArgAction;
use crate::INTERNAL_ERROR_MSG;
use crate::{Arg, Command, ValueHint};
pub(crate) fn assert_app(cmd: &Command) {
debug!("Command::_debug_asserts");
let mut short_flags = vec![];
let mut long_flags = vec![];
// Invalid version flag settings
if cmd.get_version().is_none() && cmd.get_long_version().is_none() {
// PropagateVersion is meaningless if there is no version
assert!(
!cmd.is_propagate_version_set(),
"Command {}: No version information via Command::version or Command::long_version to propagate",
cmd.get_name(),
);
// Used `Command::mut_arg("version", ..) but did not provide any version information to display
let version_needed = cmd
.get_arguments()
.filter(|x| matches!(x.get_action(), ArgAction::Version))
.map(|x| x.get_id())
.collect::<Vec<_>>();
assert_eq!(version_needed, Vec::<&str>::new(), "Command {}: `ArgAction::Version` used without providing Command::version or Command::long_version"
,cmd.get_name()
);
}
for sc in cmd.get_subcommands() {
if let Some(s) = sc.get_short_flag().as_ref() {
short_flags.push(Flag::Command(format!("-{s}"), sc.get_name()));
}
for short_alias in sc.get_all_short_flag_aliases() {
short_flags.push(Flag::Command(format!("-{short_alias}"), sc.get_name()));
}
if let Some(l) = sc.get_long_flag().as_ref() {
assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l);
long_flags.push(Flag::Command(format!("--{l}"), sc.get_name()));
}
for long_alias in sc.get_all_long_flag_aliases() {
long_flags.push(Flag::Command(format!("--{long_alias}"), sc.get_name()));
}
}
for arg in cmd.get_arguments() {
assert_arg(arg);
assert!(
!cmd.is_multicall_set(),
"Command {}: Arguments like {} cannot be set on a multicall command",
cmd.get_name(),
arg.get_id()
);
if let Some(s) = arg.get_short() {
short_flags.push(Flag::Arg(format!("-{s}"), arg.get_id().as_str()));
}
for (short_alias, _) in &arg.short_aliases {
short_flags.push(Flag::Arg(format!("-{short_alias}"), arg.get_id().as_str()));
}
if let Some(l) = arg.get_long() {
assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.get_id(), l);
long_flags.push(Flag::Arg(format!("--{l}"), arg.get_id().as_str()));
}
for (long_alias, _) in &arg.aliases {
long_flags.push(Flag::Arg(format!("--{long_alias}"), arg.get_id().as_str()));
}
// Name conflicts
if let Some((first, second)) = cmd.two_args_of(|x| x.get_id() == arg.get_id()) {
panic!(
"Command {}: Argument names must be unique, but '{}' is in use by more than one argument or group{}",
cmd.get_name(),
arg.get_id(),
duplicate_tip(cmd, first, second),
);
}
// Long conflicts
if let Some(l) = arg.get_long() {
if let Some((first, second)) = cmd.two_args_of(|x| x.get_long() == Some(l)) {
panic!(
"Command {}: Long option names must be unique for each argument, \
but '--{}' is in use by both '{}' and '{}'{}",
cmd.get_name(),
l,
first.get_id(),
second.get_id(),
duplicate_tip(cmd, first, second)
)
}
}
// Short conflicts
if let Some(s) = arg.get_short() {
if let Some((first, second)) = cmd.two_args_of(|x| x.get_short() == Some(s)) {
panic!(
"Command {}: Short option names must be unique for each argument, \
but '-{}' is in use by both '{}' and '{}'{}",
cmd.get_name(),
s,
first.get_id(),
second.get_id(),
duplicate_tip(cmd, first, second),
)
}
}
// Index conflicts
if let Some(idx) = arg.index {
if let Some((first, second)) =
cmd.two_args_of(|x| x.is_positional() && x.get_index() == Some(idx))
{
panic!(
"Command {}: Argument '{}' has the same index as '{}' \
and they are both positional arguments\n\n\t \
Use `Arg::num_args(1..)` to allow one \
positional argument to take multiple values",
cmd.get_name(),
first.get_id(),
second.get_id()
)
}
}
// requires, r_if, r_unless
for req in &arg.requires {
assert!(
cmd.id_exists(&req.1),
"Command {}: Argument or group '{}' specified in 'requires*' for '{}' does not exist",
cmd.get_name(),
req.1,
arg.get_id(),
);
}
for req in &arg.r_ifs {
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_if_eq*`",
arg.get_id()
);
assert!(
cmd.id_exists(&req.0),
"Command {}: Argument or group '{}' specified in 'required_if_eq*' for '{}' does not exist",
cmd.get_name(),
req.0,
arg.get_id()
);
}
for req in &arg.r_ifs_all {
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_if_eq_all`",
arg.get_id()
);
assert!(
cmd.id_exists(&req.0),
"Command {}: Argument or group '{}' specified in 'required_if_eq_all' for '{}' does not exist",
cmd.get_name(),
req.0,
arg.get_id()
);
}
for req in &arg.r_unless {
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_unless*`",
arg.get_id()
);
assert!(
cmd.id_exists(req),
"Command {}: Argument or group '{}' specified in 'required_unless*' for '{}' does not exist",
cmd.get_name(),
req,
arg.get_id(),
);
}
for req in &arg.r_unless_all {
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_unless*`",
arg.get_id()
);
assert!(
cmd.id_exists(req),
"Command {}: Argument or group '{}' specified in 'required_unless*' for '{}' does not exist",
cmd.get_name(),
req,
arg.get_id(),
);
}
// blacklist
for req in &arg.blacklist {
assert!(
cmd.id_exists(req),
"Command {}: Argument or group '{}' specified in 'conflicts_with*' for '{}' does not exist",
cmd.get_name(),
req,
arg.get_id(),
);
}
// overrides
for req in &arg.overrides {
assert!(
cmd.id_exists(req),
"Command {}: Argument or group '{}' specified in 'overrides_with*' for '{}' does not exist",
cmd.get_name(),
req,
arg.get_id(),
);
}
if arg.is_last_set() {
assert!(
arg.get_long().is_none(),
"Command {}: Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.",
cmd.get_name(),
arg.get_id()
);
assert!(
arg.get_short().is_none(),
"Command {}: Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.",
cmd.get_name(),
arg.get_id()
);
}
assert!(
!(arg.is_required_set() && arg.is_global_set()),
"Command {}: Global arguments cannot be required.\n\n\t'{}' is marked as both global and required",
cmd.get_name(),
arg.get_id()
);
if arg.get_value_hint() == ValueHint::CommandWithArguments {
assert!(
arg.is_positional(),
"Command {}: Argument '{}' has hint CommandWithArguments and must be positional.",
cmd.get_name(),
arg.get_id()
);
assert!(
arg.is_trailing_var_arg_set() || arg.is_last_set(),
"Command {}: Positional argument '{}' has hint CommandWithArguments, so Command must have `trailing_var_arg(true)` or `last(true)` set.",
cmd.get_name(),
arg.get_id()
);
}
}
for group in cmd.get_groups() {
// Name conflicts
assert!(
cmd.get_groups().filter(|x| x.id == group.id).count() < 2,
"Command {}: Argument group name must be unique\n\n\t'{}' is already in use",
cmd.get_name(),
group.get_id(),
);
// Groups should not have naming conflicts with Args
assert!(
!cmd.get_arguments().any(|x| x.get_id() == group.get_id()),
"Command {}: Argument group name '{}' must not conflict with argument name",
cmd.get_name(),
group.get_id(),
);
for arg in &group.args {
// Args listed inside groups should exist
assert!(
cmd.get_arguments().any(|x| x.get_id() == arg),
"Command {}: Argument group '{}' contains non-existent argument '{}'",
cmd.get_name(),
group.get_id(),
arg
);
}
for arg in &group.requires {
// Args listed inside groups should exist
assert!(
cmd.id_exists(arg),
"Command {}: Argument group '{}' requires non-existent '{}' id",
cmd.get_name(),
group.get_id(),
arg
);
}
for arg in &group.conflicts {
// Args listed inside groups should exist
assert!(
cmd.id_exists(arg),
"Command {}: Argument group '{}' conflicts with non-existent '{}' id",
cmd.get_name(),
group.get_id(),
arg
);
}
}
// Conflicts between flags and subcommands
long_flags.sort_unstable();
short_flags.sort_unstable();
detect_duplicate_flags(&long_flags, "long");
detect_duplicate_flags(&short_flags, "short");
let mut subs = FlatSet::new();
for sc in cmd.get_subcommands() {
assert!(
subs.insert(sc.get_name()),
"Command {}: command name `{}` is duplicated",
cmd.get_name(),
sc.get_name()
);
for alias in sc.get_all_aliases() {
assert!(
subs.insert(alias),
"Command {}: command `{}` alias `{}` is duplicated",
cmd.get_name(),
sc.get_name(),
alias
);
}
}
_verify_positionals(cmd);
#[cfg(feature = "help")]
if let Some(help_template) = cmd.get_help_template() {
assert!(
!help_template.to_string().contains("{flags}"),
"Command {}: {}",
cmd.get_name(),
"`{flags}` template variable was removed in clap3, they are now included in `{options}`",
);
assert!(
!help_template.to_string().contains("{unified}"),
"Command {}: {}",
cmd.get_name(),
"`{unified}` template variable was removed in clap3, use `{options}` instead"
);
#[cfg(feature = "unstable-v5")]
assert!(
!help_template.to_string().contains("{bin}"),
"Command {}: {}",
cmd.get_name(),
"`{bin}` template variable was removed in clap5, use `{name}` instead"
)
}
cmd._panic_on_missing_help(cmd.is_help_expected_set());
assert_app_flags(cmd);
}
fn duplicate_tip(cmd: &Command, first: &Arg, second: &Arg) -> &'static str {
if !cmd.is_disable_help_flag_set()
&& (first.get_id() == Id::HELP || second.get_id() == Id::HELP)
{
" (call `cmd.disable_help_flag(true)` to remove the auto-generated `--help`)"
} else if !cmd.is_disable_version_flag_set()
&& (first.get_id() == Id::VERSION || second.get_id() == Id::VERSION)
{
" (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"
} else {
""
}
}
#[derive(Eq)]
enum Flag<'a> {
Command(String, &'a str),
Arg(String, &'a str),
}
impl PartialEq for Flag<'_> {
fn eq(&self, other: &Flag) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl PartialOrd for Flag<'_> {
fn partial_cmp(&self, other: &Flag) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Flag<'_> {
fn cmp(&self, other: &Self) -> Ordering {
use Flag::*;
match (self, other) {
(Command(s1, _), Command(s2, _))
| (Arg(s1, _), Arg(s2, _))
| (Command(s1, _), Arg(s2, _))
| (Arg(s1, _), Command(s2, _)) => {
if s1 == s2 {
Ordering::Equal
} else {
s1.cmp(s2)
}
}
}
}
}
fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) {
use Flag::*;
for (one, two) in find_duplicates(flags) {
match (one, two) {
(Command(flag, one), Command(_, another)) if one != another => panic!(
"the '{flag}' {short_or_long} flag is specified for both '{one}' and '{another}' subcommands"
),
(Arg(flag, one), Arg(_, another)) if one != another => panic!(
"{short_or_long} option names must be unique, but '{flag}' is in use by both '{one}' and '{another}'"
),
(Arg(flag, arg), Command(_, sub)) | (Command(flag, sub), Arg(_, arg)) => panic!(
"the '{flag}' {short_or_long} flag for the '{arg}' argument conflicts with the short flag \
for '{sub}' subcommand"
),
_ => {}
}
}
}
/// Find duplicates in a sorted array.
///
/// The algorithm is simple: the array is sorted, duplicates
/// must be placed next to each other, we can check only adjacent elements.
fn find_duplicates<T: PartialEq>(slice: &[T]) -> impl Iterator<Item = (&T, &T)> {
slice.windows(2).filter_map(|w| {
if w[0] == w[1] {
Some((&w[0], &w[1]))
} else {
None
}
})
}
fn assert_app_flags(cmd: &Command) {
macro_rules! checker {
($a:ident requires $($b:ident)|+) => {
if cmd.$a() {
let mut s = String::new();
$(
if !cmd.$b() {
use std::fmt::Write;
write!(&mut s, " AppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a)).unwrap();
}
)+
if !s.is_empty() {
panic!("{s}")
}
}
};
($a:ident conflicts $($b:ident)|+) => {
if cmd.$a() {
let mut s = String::new();
$(
if cmd.$b() {
use std::fmt::Write;
write!(&mut s, " AppSettings::{} conflicts with AppSettings::{}.\n", std::stringify!($b), std::stringify!($a)).unwrap();
}
)+
if !s.is_empty() {
panic!("{}\n{}", cmd.get_name(), s)
}
}
};
}
checker!(is_multicall_set conflicts is_no_binary_name_set);
}
#[cfg(debug_assertions)]
fn _verify_positionals(cmd: &Command) -> bool {
debug!("Command::_verify_positionals");
// Because you must wait until all arguments have been supplied, this is the first chance
// to make assertions on positional argument indexes
//
// First we verify that the index highest supplied index, is equal to the number of
// positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
// but no 2)
let highest_idx = cmd
.get_keymap()
.keys()
.filter_map(|x| {
if let KeyType::Position(n) = x {
Some(*n)
} else {
None
}
})
.max()
.unwrap_or(0);
let num_p = cmd.get_keymap().keys().filter(|x| x.is_position()).count();
assert!(
highest_idx == num_p,
"Found positional argument whose index is {highest_idx} but there \
are only {num_p} positional arguments defined",
);
for arg in cmd.get_arguments() {
if arg.index.unwrap_or(0) == highest_idx {
assert!(
!arg.is_trailing_var_arg_set() || !arg.is_last_set(),
"{}:{}: `Arg::trailing_var_arg` and `Arg::last` cannot be used together",
cmd.get_name(),
arg.get_id()
);
if arg.is_trailing_var_arg_set() {
assert!(
arg.is_multiple(),
"{}:{}: `Arg::trailing_var_arg` must accept multiple values",
cmd.get_name(),
arg.get_id()
);
}
} else {
assert!(
!arg.is_trailing_var_arg_set(),
"{}:{}: `Arg::trailing_var_arg` can only apply to last positional",
cmd.get_name(),
arg.get_id()
);
}
}
// Next we verify that only the highest index has takes multiple arguments (if any)
let only_highest = |a: &Arg| a.is_multiple() && (a.get_index().unwrap_or(0) != highest_idx);
if cmd.get_positionals().any(only_highest) {
// First we make sure if there is a positional that allows multiple values
// the one before it (second to last) has one of these:
// * a value terminator
// * ArgSettings::Last
// * The last arg is Required
// We can't pass the closure (it.next()) to the macro directly because each call to
// find() (iterator, not macro) gets called repeatedly.
let last = &cmd.get_keymap()[&KeyType::Position(highest_idx)];
let second_to_last = &cmd.get_keymap()[&KeyType::Position(highest_idx - 1)];
// Either the final positional is required
// Or the second to last has a terminator or .last(true) set
let ok = last.is_required_set()
|| (second_to_last.terminator.is_some() || second_to_last.is_last_set())
|| last.is_last_set();
assert!(
ok,
"Positional argument `{last}` *must* have `required(true)` or `last(true)` set \
because a prior positional argument (`{second_to_last}`) has `num_args(1..)`"
);
// We make sure if the second to last is Multiple the last is ArgSettings::Last
let ok = second_to_last.is_multiple() || last.is_last_set();
assert!(
ok,
"Only the last positional argument, or second to last positional \
argument may be set to `.num_args(1..)`"
);
// Next we check how many have both Multiple and not a specific number of values set
let count = cmd
.get_positionals()
.filter(|p| {
p.is_multiple_values_set()
&& p.get_value_terminator().is_none()
&& !p.get_num_args().expect(INTERNAL_ERROR_MSG).is_fixed()
})
.count();
let ok = count <= 1
|| (last.is_last_set()
&& last.is_multiple()
&& second_to_last.is_multiple()
&& count == 2);
assert!(
ok,
"Only one positional argument with `.num_args(1..)` set is allowed per \
command, unless the second one also has .last(true) set"
);
}
let mut found = false;
if cmd.is_allow_missing_positional_set() {
// Check that if a required positional argument is found, all positions with a lower
// index are also required.
let mut foundx2 = false;
for p in cmd.get_positionals() {
if foundx2 && !p.is_required_set() {
assert!(
p.is_required_set(),
"Found non-required positional argument with a lower \
index than a required positional argument by two or more: {:?} \
index {:?}",
p.get_id(),
p.get_index()
);
} else if p.is_required_set() && !p.is_last_set() {
// Args that .last(true) don't count since they can be required and have
// positionals with a lower index that aren't required
// Imagine: prog <req1> [opt1] -- <req2>
// Both of these are valid invocations:
// $ prog r1 -- r2
// $ prog r1 o1 -- r2
if found {
foundx2 = true;
continue;
}
found = true;
continue;
} else {
found = false;
}
}
} else {
// Check that if a required positional argument is found, all positions with a lower
// index are also required
for p in (1..=num_p).rev().filter_map(|n| cmd.get_keymap().get(&n)) {
if found {
assert!(
p.is_required_set(),
"Found non-required positional argument with a lower \
index than a required positional argument: {:?} index {:?}",
p.get_id(),
p.get_index()
);
} else if p.is_required_set() && !p.is_last_set() {
// Args that .last(true) don't count since they can be required and have
// positionals with a lower index that aren't required
// Imagine: prog <req1> [opt1] -- <req2>
// Both of these are valid invocations:
// $ prog r1 -- r2
// $ prog r1 o1 -- r2
found = true;
continue;
}
}
}
assert!(
cmd.get_positionals().filter(|p| p.is_last_set()).count() < 2,
"Only one positional argument may have last(true) set. Found two."
);
if cmd
.get_positionals()
.any(|p| p.is_last_set() && p.is_required_set())
&& cmd.has_subcommands()
&& !cmd.is_subcommand_negates_reqs_set()
{
panic!(
"Having a required positional argument with .last(true) set *and* child \
subcommands without setting SubcommandsNegateReqs isn't compatible."
);
}
true
}
fn assert_arg(arg: &Arg) {
debug!("Arg::_debug_asserts:{}", arg.get_id());
// Self conflict
// TODO: this check should be recursive
assert!(
!arg.blacklist.iter().any(|x| x == arg.get_id()),
"Argument '{}' cannot conflict with itself",
arg.get_id(),
);
assert_eq!(
arg.get_action().takes_values(),
arg.is_takes_value_set(),
"Argument `{}`'s selected action {:?} contradicts `takes_value`",
arg.get_id(),
arg.get_action()
);
if let Some(action_type_id) = arg.get_action().value_type_id() {
assert_eq!(
action_type_id,
arg.get_value_parser().type_id(),
"Argument `{}`'s selected action {:?} contradicts `value_parser` ({:?})",
arg.get_id(),
arg.get_action(),
arg.get_value_parser()
);
}
if arg.get_value_hint() != ValueHint::Unknown {
assert!(
arg.is_takes_value_set(),
"Argument '{}' has value hint but takes no value",
arg.get_id()
);
if arg.get_value_hint() == ValueHint::CommandWithArguments {
assert!(
arg.is_multiple_values_set(),
"Argument '{}' uses hint CommandWithArguments and must accept multiple values",
arg.get_id()
)
}
}
if arg.index.is_some() {
assert!(
arg.is_positional(),
"Argument '{}' is a positional argument and can't have short or long name versions",
arg.get_id()
);
assert!(
arg.is_takes_value_set(),
"Argument '{}` is positional and it must take a value but action is {:?}{}",
arg.get_id(),
arg.get_action(),
if arg.get_id() == Id::HELP {
" (`mut_arg` no longer works with implicit `--help`)"
} else if arg.get_id() == Id::VERSION {
" (`mut_arg` no longer works with implicit `--version`)"
} else {
""
}
);
}
let num_vals = arg.get_num_args().expect(INTERNAL_ERROR_MSG);
// This can be the cause of later asserts, so put this first
if num_vals != ValueRange::EMPTY {
// HACK: Don't check for flags to make the derive easier
let num_val_names = arg.get_value_names().unwrap_or(&[]).len();
if num_vals.max_values() < num_val_names {
panic!(
"Argument {}: Too many value names ({}) compared to `num_args` ({})",
arg.get_id(),
num_val_names,
num_vals
);
}
}
assert_eq!(
num_vals.takes_values(),
arg.is_takes_value_set(),
"Argument {}: mismatch between `num_args` ({}) and `takes_value`",
arg.get_id(),
num_vals,
);
assert_eq!(
num_vals.is_multiple(),
arg.is_multiple_values_set(),
"Argument {}: mismatch between `num_args` ({}) and `multiple_values`",
arg.get_id(),
num_vals,
);
if 1 < num_vals.min_values() {
assert!(
!arg.is_require_equals_set(),
"Argument {}: cannot accept more than 1 arg (num_args={}) with require_equals",
arg.get_id(),
num_vals
);
}
if num_vals == ValueRange::SINGLE {
assert!(
!arg.is_multiple_values_set(),
"Argument {}: mismatch between `num_args` and `multiple_values`",
arg.get_id()
);
}
assert_arg_flags(arg);
}
fn assert_arg_flags(arg: &Arg) {
macro_rules! checker {
($a:ident requires $($b:ident)|+) => {
if arg.$a() {
let mut s = String::new();
$(
if !arg.$b() {
use std::fmt::Write;
write!(&mut s, " Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a)).unwrap();
}
)+
if !s.is_empty() {
panic!("Argument {:?}\n{}", arg.get_id(), s)
}
}
}
}
checker!(is_hide_possible_values_set requires is_takes_value_set);
checker!(is_allow_hyphen_values_set requires is_takes_value_set);
checker!(is_allow_negative_numbers_set requires is_takes_value_set);
checker!(is_require_equals_set requires is_takes_value_set);
checker!(is_last_set requires is_takes_value_set);
checker!(is_hide_default_value_set requires is_takes_value_set);
checker!(is_multiple_values_set requires is_takes_value_set);
checker!(is_ignore_case_set requires is_takes_value_set);
}

216
vendor/clap_builder/src/builder/ext.rs vendored Normal file
View File

@ -0,0 +1,216 @@
use crate::util::AnyValueId;
use crate::util::FlatMap;
#[derive(Default, Clone, Debug)]
pub(crate) struct Extensions {
extensions: FlatMap<AnyValueId, BoxedExtension>,
}
impl Extensions {
#[allow(dead_code)]
pub(crate) fn get<T: Extension>(&self) -> Option<&T> {
let id = AnyValueId::of::<T>();
self.extensions.get(&id).map(|e| e.as_ref::<T>())
}
#[allow(dead_code)]
pub(crate) fn get_mut<T: Extension>(&mut self) -> Option<&mut T> {
let id = AnyValueId::of::<T>();
self.extensions.get_mut(&id).map(|e| e.as_mut::<T>())
}
#[allow(dead_code)]
pub(crate) fn get_or_insert_default<T: Extension + Default>(&mut self) -> &mut T {
let id = AnyValueId::of::<T>();
self.extensions
.entry(id)
.or_insert_with(|| BoxedExtension::new(T::default()))
.as_mut::<T>()
}
#[allow(dead_code)]
pub(crate) fn set<T: Extension + Into<BoxedEntry>>(&mut self, tagged: T) -> bool {
let BoxedEntry { id, value } = tagged.into();
self.extensions.insert(id, value).is_some()
}
#[allow(dead_code)]
pub(crate) fn remove<T: Extension>(&mut self) -> Option<Box<dyn Extension>> {
let id = AnyValueId::of::<T>();
self.extensions.remove(&id).map(BoxedExtension::into_inner)
}
pub(crate) fn update(&mut self, other: &Self) {
for (key, value) in other.extensions.iter() {
self.extensions.insert(*key, value.clone());
}
}
}
/// Supports conversion to `Any`. Traits to be extended by `impl_downcast!` must extend `Extension`.
pub(crate) trait Extension: std::fmt::Debug + Send + Sync + 'static {
/// Convert `Box<dyn Trait>` (where `Trait: Extension`) to `Box<dyn Any>`.
///
/// `Box<dyn Any>` can /// then be further `downcast` into
/// `Box<ConcreteType>` where `ConcreteType` implements `Trait`.
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any>;
/// Clone `&Box<dyn Trait>` (where `Trait: Extension`) to `Box<dyn Extension>`.
///
/// `Box<dyn Any>` can /// then be further `downcast` into
// `Box<ConcreteType>` where `ConcreteType` implements `Trait`.
fn clone_extension(&self) -> Box<dyn Extension>;
/// Convert `&Trait` (where `Trait: Extension`) to `&Any`.
///
/// This is needed since Rust cannot /// generate `&Any`'s vtable from
/// `&Trait`'s.
fn as_any(&self) -> &dyn std::any::Any;
/// Convert `&mut Trait` (where `Trait: Extension`) to `&Any`.
///
/// This is needed since Rust cannot /// generate `&mut Any`'s vtable from
/// `&mut Trait`'s.
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}
impl<T> Extension for T
where
T: Clone + std::fmt::Debug + Send + Sync + 'static,
{
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn clone_extension(&self) -> Box<dyn Extension> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl Clone for Box<dyn Extension> {
fn clone(&self) -> Self {
self.as_ref().clone_extension()
}
}
#[derive(Clone)]
#[repr(transparent)]
struct BoxedExtension(Box<dyn Extension>);
impl BoxedExtension {
fn new<T: Extension>(inner: T) -> Self {
Self(Box::new(inner))
}
fn into_inner(self) -> Box<dyn Extension> {
self.0
}
fn as_ref<T: Extension>(&self) -> &T {
self.0.as_ref().as_any().downcast_ref::<T>().unwrap()
}
fn as_mut<T: Extension>(&mut self) -> &mut T {
self.0.as_mut().as_any_mut().downcast_mut::<T>().unwrap()
}
}
impl std::fmt::Debug for BoxedExtension {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.0.fmt(f)
}
}
#[derive(Clone)]
pub(crate) struct BoxedEntry {
id: AnyValueId,
value: BoxedExtension,
}
impl BoxedEntry {
pub(crate) fn new(r: impl Extension) -> Self {
let id = AnyValueId::from(&r);
let value = BoxedExtension::new(r);
BoxedEntry { id, value }
}
}
impl<R: Extension> From<R> for BoxedEntry {
fn from(inner: R) -> Self {
BoxedEntry::new(inner)
}
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
struct Number(usize);
#[test]
fn get() {
let mut ext = Extensions::default();
ext.set(Number(10));
assert_eq!(ext.get::<Number>(), Some(&Number(10)));
}
#[test]
fn get_mut() {
let mut ext = Extensions::default();
ext.set(Number(10));
*ext.get_mut::<Number>().unwrap() = Number(20);
assert_eq!(ext.get::<Number>(), Some(&Number(20)));
}
#[test]
fn get_or_insert_default_empty() {
let mut ext = Extensions::default();
assert_eq!(ext.get_or_insert_default::<Number>(), &Number(0));
}
#[test]
fn get_or_insert_default_full() {
let mut ext = Extensions::default();
ext.set(Number(10));
assert_eq!(ext.get_or_insert_default::<Number>(), &Number(10));
}
#[test]
fn set() {
let mut ext = Extensions::default();
assert!(!ext.set(Number(10)));
assert_eq!(ext.get::<Number>(), Some(&Number(10)));
assert!(ext.set(Number(20)));
assert_eq!(ext.get::<Number>(), Some(&Number(20)));
}
#[test]
fn reset() {
let mut ext = Extensions::default();
assert_eq!(ext.get::<Number>(), None);
assert!(ext.remove::<Number>().is_none());
assert_eq!(ext.get::<Number>(), None);
assert!(!ext.set(Number(10)));
assert_eq!(ext.get::<Number>(), Some(&Number(10)));
assert!(ext.remove::<Number>().is_some());
assert_eq!(ext.get::<Number>(), None);
}
#[test]
fn update() {
let mut ext = Extensions::default();
assert_eq!(ext.get::<Number>(), None);
let mut new = Extensions::default();
assert!(!new.set(Number(10)));
ext.update(&new);
assert_eq!(ext.get::<Number>(), Some(&Number(10)));
}
}

67
vendor/clap_builder/src/builder/mod.rs vendored Normal file
View File

@ -0,0 +1,67 @@
//! Define [`Command`] line [arguments][`Arg`]
mod action;
mod app_settings;
mod arg;
mod arg_group;
mod arg_predicate;
mod arg_settings;
mod command;
mod ext;
mod os_str;
mod possible_value;
mod range;
mod resettable;
mod str;
mod styled_str;
mod value_hint;
mod value_parser;
#[cfg(debug_assertions)]
mod debug_asserts;
#[cfg(test)]
mod tests;
pub mod styling;
pub use self::str::Str;
pub use action::ArgAction;
pub use arg::Arg;
pub use arg_group::ArgGroup;
pub use arg_predicate::ArgPredicate;
pub use command::Command;
pub use os_str::OsStr;
pub use possible_value::PossibleValue;
pub use range::ValueRange;
pub use resettable::IntoResettable;
pub use resettable::Resettable;
pub use styled_str::StyledStr;
pub use styling::Styles;
pub use value_hint::ValueHint;
pub use value_parser::_AutoValueParser;
pub use value_parser::via_prelude;
pub use value_parser::BoolValueParser;
pub use value_parser::BoolishValueParser;
pub use value_parser::EnumValueParser;
pub use value_parser::FalseyValueParser;
pub use value_parser::MapValueParser;
pub use value_parser::NonEmptyStringValueParser;
pub use value_parser::OsStringValueParser;
pub use value_parser::PathBufValueParser;
pub use value_parser::PossibleValuesParser;
pub use value_parser::RangedI64ValueParser;
pub use value_parser::RangedU64ValueParser;
pub use value_parser::StringValueParser;
pub use value_parser::TryMapValueParser;
pub use value_parser::TypedValueParser;
pub use value_parser::UnknownArgumentValueParser;
pub use value_parser::ValueParser;
pub use value_parser::ValueParserFactory;
pub use value_parser::_AnonymousValueParser;
#[allow(unused_imports)]
pub(crate) use self::str::Inner as StrInner;
pub(crate) use action::CountType;
pub(crate) use arg_settings::{ArgFlags, ArgSettings};
pub(crate) use command::AppTag;

View File

@ -0,0 +1,336 @@
use crate::builder::Str;
/// A UTF-8-encoded fixed string
///
/// **NOTE:** To support dynamic values (i.e. `OsString`), enable the `string`
/// feature
#[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct OsStr {
name: Inner,
}
impl OsStr {
#[cfg(feature = "string")]
pub(crate) fn from_string(name: std::ffi::OsString) -> Self {
Self {
name: Inner::from_string(name),
}
}
#[cfg(feature = "string")]
pub(crate) fn from_ref(name: &std::ffi::OsStr) -> Self {
Self {
name: Inner::from_ref(name),
}
}
pub(crate) fn from_static_ref(name: &'static std::ffi::OsStr) -> Self {
Self {
name: Inner::from_static_ref(name),
}
}
/// Get the raw string as an `std::ffi::OsStr`
pub fn as_os_str(&self) -> &std::ffi::OsStr {
self.name.as_os_str()
}
/// Get the raw string as an `OsString`
pub fn to_os_string(&self) -> std::ffi::OsString {
self.as_os_str().to_owned()
}
}
impl From<&'_ OsStr> for OsStr {
fn from(id: &'_ OsStr) -> Self {
id.clone()
}
}
#[cfg(feature = "string")]
impl From<Str> for OsStr {
fn from(id: Str) -> Self {
match id.into_inner() {
crate::builder::StrInner::Static(s) => Self::from_static_ref(std::ffi::OsStr::new(s)),
crate::builder::StrInner::Owned(s) => Self::from_ref(std::ffi::OsStr::new(s.as_ref())),
}
}
}
#[cfg(not(feature = "string"))]
impl From<Str> for OsStr {
fn from(id: Str) -> Self {
Self::from_static_ref(std::ffi::OsStr::new(id.into_inner().0))
}
}
#[cfg(feature = "perf")]
impl From<&'_ Str> for OsStr {
fn from(id: &'_ Str) -> Self {
match id.clone().into_inner() {
crate::builder::StrInner::Static(s) => Self::from_static_ref(std::ffi::OsStr::new(s)),
crate::builder::StrInner::Owned(s) => Self::from_ref(std::ffi::OsStr::new(s.as_ref())),
}
}
}
impl From<&'_ Str> for OsStr {
fn from(id: &'_ Str) -> Self {
id.clone().into()
}
}
#[cfg(feature = "string")]
impl From<std::ffi::OsString> for OsStr {
fn from(name: std::ffi::OsString) -> Self {
Self::from_string(name)
}
}
#[cfg(feature = "string")]
impl From<&'_ std::ffi::OsString> for OsStr {
fn from(name: &'_ std::ffi::OsString) -> Self {
Self::from_ref(name.as_os_str())
}
}
#[cfg(feature = "string")]
impl From<std::string::String> for OsStr {
fn from(name: std::string::String) -> Self {
Self::from_string(name.into())
}
}
#[cfg(feature = "string")]
impl From<&'_ std::string::String> for OsStr {
fn from(name: &'_ std::string::String) -> Self {
Self::from_ref(name.as_str().as_ref())
}
}
impl From<&'static std::ffi::OsStr> for OsStr {
fn from(name: &'static std::ffi::OsStr) -> Self {
Self::from_static_ref(name)
}
}
impl From<&'_ &'static std::ffi::OsStr> for OsStr {
fn from(name: &'_ &'static std::ffi::OsStr) -> Self {
Self::from_static_ref(name)
}
}
impl From<&'static str> for OsStr {
fn from(name: &'static str) -> Self {
Self::from_static_ref(name.as_ref())
}
}
impl From<&'_ &'static str> for OsStr {
fn from(name: &'_ &'static str) -> Self {
Self::from_static_ref((*name).as_ref())
}
}
impl From<OsStr> for std::ffi::OsString {
fn from(name: OsStr) -> Self {
name.name.into_os_string()
}
}
impl From<OsStr> for std::path::PathBuf {
fn from(name: OsStr) -> Self {
std::ffi::OsString::from(name).into()
}
}
impl std::fmt::Debug for OsStr {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self.as_os_str(), f)
}
}
impl std::ops::Deref for OsStr {
type Target = std::ffi::OsStr;
#[inline]
fn deref(&self) -> &std::ffi::OsStr {
self.as_os_str()
}
}
impl AsRef<std::ffi::OsStr> for OsStr {
#[inline]
fn as_ref(&self) -> &std::ffi::OsStr {
self.as_os_str()
}
}
impl AsRef<std::path::Path> for OsStr {
#[inline]
fn as_ref(&self) -> &std::path::Path {
std::path::Path::new(self)
}
}
impl std::borrow::Borrow<std::ffi::OsStr> for OsStr {
#[inline]
fn borrow(&self) -> &std::ffi::OsStr {
self.as_os_str()
}
}
impl PartialEq<str> for OsStr {
#[inline]
fn eq(&self, other: &str) -> bool {
PartialEq::eq(self.as_os_str(), other)
}
}
impl PartialEq<OsStr> for str {
#[inline]
fn eq(&self, other: &OsStr) -> bool {
PartialEq::eq(self, other.as_os_str())
}
}
impl PartialEq<&'_ str> for OsStr {
#[inline]
fn eq(&self, other: &&str) -> bool {
PartialEq::eq(self.as_os_str(), *other)
}
}
impl PartialEq<OsStr> for &'_ str {
#[inline]
fn eq(&self, other: &OsStr) -> bool {
PartialEq::eq(*self, other.as_os_str())
}
}
impl PartialEq<&'_ std::ffi::OsStr> for OsStr {
#[inline]
fn eq(&self, other: &&std::ffi::OsStr) -> bool {
PartialEq::eq(self.as_os_str(), *other)
}
}
impl PartialEq<OsStr> for &'_ std::ffi::OsStr {
#[inline]
fn eq(&self, other: &OsStr) -> bool {
PartialEq::eq(*self, other.as_os_str())
}
}
impl PartialEq<std::string::String> for OsStr {
#[inline]
fn eq(&self, other: &std::string::String) -> bool {
PartialEq::eq(self.as_os_str(), other.as_str())
}
}
impl PartialEq<OsStr> for std::string::String {
#[inline]
fn eq(&self, other: &OsStr) -> bool {
PartialEq::eq(self.as_str(), other.as_os_str())
}
}
impl PartialEq<std::ffi::OsString> for OsStr {
#[inline]
fn eq(&self, other: &std::ffi::OsString) -> bool {
PartialEq::eq(self.as_os_str(), other.as_os_str())
}
}
impl PartialEq<OsStr> for std::ffi::OsString {
#[inline]
fn eq(&self, other: &OsStr) -> bool {
PartialEq::eq(self.as_os_str(), other.as_os_str())
}
}
#[cfg(feature = "string")]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) enum Inner {
Static(&'static std::ffi::OsStr),
Owned(Box<std::ffi::OsStr>),
}
impl Inner {
pub(crate) fn from_string(name: std::ffi::OsString) -> Self {
Self::Owned(name.into_boxed_os_str())
}
pub(crate) fn from_ref(name: &std::ffi::OsStr) -> Self {
Self::Owned(Box::from(name))
}
pub(crate) fn from_static_ref(name: &'static std::ffi::OsStr) -> Self {
Self::Static(name)
}
pub(crate) fn as_os_str(&self) -> &std::ffi::OsStr {
match self {
Self::Static(s) => s,
Self::Owned(s) => s.as_ref(),
}
}
pub(crate) fn into_os_string(self) -> std::ffi::OsString {
self.as_os_str().to_owned()
}
}
}
#[cfg(not(feature = "string"))]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) struct Inner(&'static std::ffi::OsStr);
impl Inner {
pub(crate) fn from_static_ref(name: &'static std::ffi::OsStr) -> Self {
Self(name)
}
pub(crate) fn as_os_str(&self) -> &std::ffi::OsStr {
self.0
}
pub(crate) fn into_os_string(self) -> std::ffi::OsString {
self.as_os_str().to_owned()
}
}
}
pub(crate) use inner::Inner;
impl Default for Inner {
fn default() -> Self {
Self::from_static_ref(std::ffi::OsStr::new(""))
}
}
impl PartialEq for Inner {
fn eq(&self, other: &Inner) -> bool {
self.as_os_str() == other.as_os_str()
}
}
impl PartialOrd for Inner {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Inner {
fn cmp(&self, other: &Inner) -> std::cmp::Ordering {
self.as_os_str().cmp(other.as_os_str())
}
}
impl Eq for Inner {}
impl std::hash::Hash for Inner {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_os_str().hash(state);
}
}

View File

@ -0,0 +1,229 @@
use crate::builder::IntoResettable;
use crate::builder::Str;
use crate::builder::StyledStr;
use crate::util::eq_ignore_case;
/// A possible value of an argument.
///
/// This is used for specifying [possible values] of [Args].
///
/// See also [`PossibleValuesParser`][crate::builder::PossibleValuesParser]
///
/// **NOTE:** Most likely you can use strings, rather than `PossibleValue` as it is only required
/// to [hide] single values from help messages and shell completions or to attach [help] to
/// possible values.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Arg, builder::PossibleValue, ArgAction};
/// let cfg = Arg::new("config")
/// .action(ArgAction::Set)
/// .value_name("FILE")
/// .value_parser([
/// PossibleValue::new("fast"),
/// PossibleValue::new("slow").help("slower than fast"),
/// PossibleValue::new("secret speed").hide(true)
/// ]);
/// ```
///
/// [Args]: crate::Arg
/// [possible values]: crate::builder::ValueParser::possible_values
/// [hide]: PossibleValue::hide()
/// [help]: PossibleValue::help()
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct PossibleValue {
name: Str,
help: Option<StyledStr>,
aliases: Vec<Str>, // (name, visible)
hide: bool,
}
impl PossibleValue {
/// Create a [`PossibleValue`] with its name.
///
/// The name will be used to decide whether this value was provided by the user to an argument.
///
/// **NOTE:** In case it is not [hidden] it will also be shown in help messages for arguments
/// that use it as a [possible value] and have not hidden them through [`Arg::hide_possible_values(true)`].
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::PossibleValue;
/// PossibleValue::new("fast")
/// # ;
/// ```
/// [hidden]: PossibleValue::hide
/// [possible value]: crate::builder::PossibleValuesParser
/// [`Arg::hide_possible_values(true)`]: crate::Arg::hide_possible_values()
pub fn new(name: impl Into<Str>) -> Self {
PossibleValue {
name: name.into(),
..Default::default()
}
}
/// Sets the help description of the value.
///
/// This is typically displayed in completions (where supported) and should be a short, one-line
/// description.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::PossibleValue;
/// PossibleValue::new("slow")
/// .help("not fast")
/// # ;
/// ```
#[inline]
#[must_use]
pub fn help(mut self, help: impl IntoResettable<StyledStr>) -> Self {
self.help = help.into_resettable().into_option();
self
}
/// Hides this value from help and shell completions.
///
/// This is an alternative to hiding through [`Arg::hide_possible_values(true)`], if you only
/// want to hide some values.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::PossibleValue;
/// PossibleValue::new("secret")
/// .hide(true)
/// # ;
/// ```
/// [`Arg::hide_possible_values(true)`]: crate::Arg::hide_possible_values()
#[inline]
#[must_use]
pub fn hide(mut self, yes: bool) -> Self {
self.hide = yes;
self
}
/// Sets a *hidden* alias for this argument value.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::PossibleValue;
/// PossibleValue::new("slow")
/// .alias("not-fast")
/// # ;
/// ```
#[must_use]
pub fn alias(mut self, name: impl IntoResettable<Str>) -> Self {
if let Some(name) = name.into_resettable().into_option() {
self.aliases.push(name);
} else {
self.aliases.clear();
}
self
}
/// Sets multiple *hidden* aliases for this argument value.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::PossibleValue;
/// PossibleValue::new("slow")
/// .aliases(["not-fast", "snake-like"])
/// # ;
/// ```
#[must_use]
pub fn aliases(mut self, names: impl IntoIterator<Item = impl Into<Str>>) -> Self {
self.aliases.extend(names.into_iter().map(|a| a.into()));
self
}
}
/// Reflection
impl PossibleValue {
/// Get the name of the argument value
#[inline]
pub fn get_name(&self) -> &str {
self.name.as_str()
}
/// Get the help specified for this argument, if any
#[inline]
pub fn get_help(&self) -> Option<&StyledStr> {
self.help.as_ref()
}
/// Report if [`PossibleValue::hide`] is set
#[inline]
pub fn is_hide_set(&self) -> bool {
self.hide
}
/// Report if PossibleValue is not hidden and has a help message
pub(crate) fn should_show_help(&self) -> bool {
!self.hide && self.help.is_some()
}
/// Get the name if argument value is not hidden, `None` otherwise,
/// but wrapped in quotes if it contains whitespace
#[cfg(feature = "help")]
pub(crate) fn get_visible_quoted_name(&self) -> Option<std::borrow::Cow<'_, str>> {
if !self.hide {
Some(if self.name.contains(char::is_whitespace) {
format!("{:?}", self.name).into()
} else {
self.name.as_str().into()
})
} else {
None
}
}
/// Returns all valid values of the argument value.
///
/// Namely the name and all aliases.
pub fn get_name_and_aliases(&self) -> impl Iterator<Item = &str> + '_ {
std::iter::once(self.get_name()).chain(self.aliases.iter().map(|s| s.as_str()))
}
/// Tests if the value is valid for this argument value
///
/// The value is valid if it is either the name or one of the aliases.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::PossibleValue;
/// let arg_value = PossibleValue::new("fast").alias("not-slow");
///
/// assert!(arg_value.matches("fast", false));
/// assert!(arg_value.matches("not-slow", false));
///
/// assert!(arg_value.matches("FAST", true));
/// assert!(!arg_value.matches("FAST", false));
/// ```
pub fn matches(&self, value: &str, ignore_case: bool) -> bool {
if ignore_case {
self.get_name_and_aliases()
.any(|name| eq_ignore_case(name, value))
} else {
self.get_name_and_aliases().any(|name| name == value)
}
}
}
impl<S: Into<Str>> From<S> for PossibleValue {
fn from(s: S) -> Self {
Self::new(s)
}
}

286
vendor/clap_builder/src/builder/range.rs vendored Normal file
View File

@ -0,0 +1,286 @@
/// Values per occurrence for an argument
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ValueRange {
start_inclusive: usize,
end_inclusive: usize,
}
impl ValueRange {
/// Nor argument values, or a flag
pub const EMPTY: Self = Self {
start_inclusive: 0,
end_inclusive: 0,
};
/// A single argument value, the most common case for options
pub const SINGLE: Self = Self {
start_inclusive: 1,
end_inclusive: 1,
};
/// Create a range
///
/// # Panics
///
/// If the end is less than the start (debug builds)
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::ValueRange;
/// let range = ValueRange::new(5);
/// let range = ValueRange::new(5..10);
/// let range = ValueRange::new(5..=10);
/// let range = ValueRange::new(5..);
/// let range = ValueRange::new(..10);
/// let range = ValueRange::new(..=10);
/// ```
///
/// While this will panic:
/// ```should_panic
/// # use clap_builder as clap;
/// # use clap::builder::ValueRange;
/// let range = ValueRange::new(10..5); // Panics!
/// ```
pub fn new(range: impl Into<Self>) -> Self {
range.into()
}
pub(crate) fn raw(start_inclusive: usize, end_inclusive: usize) -> Self {
debug_assert!(start_inclusive <= end_inclusive);
Self {
start_inclusive,
end_inclusive,
}
}
/// Fewest number of values the argument accepts
pub fn min_values(&self) -> usize {
self.start_inclusive
}
/// Most number of values the argument accepts
pub fn max_values(&self) -> usize {
self.end_inclusive
}
/// Report whether the argument takes any values (ie is a flag)
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::ValueRange;
/// let range = ValueRange::new(5);
/// assert!(range.takes_values());
///
/// let range = ValueRange::new(0);
/// assert!(!range.takes_values());
/// ```
pub fn takes_values(&self) -> bool {
self.end_inclusive != 0
}
pub(crate) fn is_unbounded(&self) -> bool {
self.end_inclusive == usize::MAX
}
pub(crate) fn is_fixed(&self) -> bool {
self.start_inclusive == self.end_inclusive
}
pub(crate) fn is_multiple(&self) -> bool {
self.start_inclusive != self.end_inclusive || 1 < self.start_inclusive
}
pub(crate) fn num_values(&self) -> Option<usize> {
self.is_fixed().then_some(self.start_inclusive)
}
pub(crate) fn accepts_more(&self, current: usize) -> bool {
current < self.end_inclusive
}
}
impl std::ops::RangeBounds<usize> for ValueRange {
fn start_bound(&self) -> std::ops::Bound<&usize> {
std::ops::Bound::Included(&self.start_inclusive)
}
fn end_bound(&self) -> std::ops::Bound<&usize> {
std::ops::Bound::Included(&self.end_inclusive)
}
}
impl Default for ValueRange {
fn default() -> Self {
Self::SINGLE
}
}
impl From<usize> for ValueRange {
fn from(fixed: usize) -> Self {
(fixed..=fixed).into()
}
}
impl From<std::ops::Range<usize>> for ValueRange {
fn from(range: std::ops::Range<usize>) -> Self {
let start_inclusive = range.start;
let end_inclusive = range.end.saturating_sub(1);
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<std::ops::RangeFull> for ValueRange {
fn from(_: std::ops::RangeFull) -> Self {
let start_inclusive = 0;
let end_inclusive = usize::MAX;
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<std::ops::RangeFrom<usize>> for ValueRange {
fn from(range: std::ops::RangeFrom<usize>) -> Self {
let start_inclusive = range.start;
let end_inclusive = usize::MAX;
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<std::ops::RangeTo<usize>> for ValueRange {
fn from(range: std::ops::RangeTo<usize>) -> Self {
let start_inclusive = 0;
let end_inclusive = range.end.saturating_sub(1);
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<std::ops::RangeInclusive<usize>> for ValueRange {
fn from(range: std::ops::RangeInclusive<usize>) -> Self {
let start_inclusive = *range.start();
let end_inclusive = *range.end();
Self::raw(start_inclusive, end_inclusive)
}
}
impl From<std::ops::RangeToInclusive<usize>> for ValueRange {
fn from(range: std::ops::RangeToInclusive<usize>) -> Self {
let start_inclusive = 0;
let end_inclusive = range.end;
Self::raw(start_inclusive, end_inclusive)
}
}
impl std::fmt::Display for ValueRange {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
ok!(self.start_inclusive.fmt(f));
if !self.is_fixed() {
ok!("..=".fmt(f));
ok!(self.end_inclusive.fmt(f));
}
Ok(())
}
}
impl std::fmt::Debug for ValueRange {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{self}")
}
}
#[cfg(test)]
mod test {
use super::*;
use std::ops::RangeBounds;
#[test]
fn from_fixed() {
let range: ValueRange = 5.into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&5));
assert!(range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), Some(5));
assert!(range.takes_values());
}
#[test]
fn from_fixed_empty() {
let range: ValueRange = 0.into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&0));
assert!(range.is_fixed());
assert!(!range.is_multiple());
assert_eq!(range.num_values(), Some(0));
assert!(!range.takes_values());
}
#[test]
fn from_range() {
let range: ValueRange = (5..10).into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&9));
assert!(!range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), None);
assert!(range.takes_values());
}
#[test]
fn from_range_inclusive() {
let range: ValueRange = (5..=10).into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&10));
assert!(!range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), None);
assert!(range.takes_values());
}
#[test]
fn from_range_full() {
let range: ValueRange = (..).into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX));
assert!(!range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), None);
assert!(range.takes_values());
}
#[test]
fn from_range_from() {
let range: ValueRange = (5..).into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX));
assert!(!range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), None);
assert!(range.takes_values());
}
#[test]
fn from_range_to() {
let range: ValueRange = (..10).into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&9));
assert!(!range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), None);
assert!(range.takes_values());
}
#[test]
fn from_range_to_inclusive() {
let range: ValueRange = (..=10).into();
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
assert_eq!(range.end_bound(), std::ops::Bound::Included(&10));
assert!(!range.is_fixed());
assert!(range.is_multiple());
assert_eq!(range.num_values(), None);
assert!(range.takes_values());
}
}

View File

@ -0,0 +1,212 @@
// Unlike `impl Into<Option<T>>` or `Option<impl Into<T>>`, this isn't ambiguous for the `None`
// case.
use crate::builder::ArgAction;
use crate::builder::OsStr;
use crate::builder::Str;
use crate::builder::StyledStr;
use crate::builder::ValueHint;
use crate::builder::ValueParser;
use crate::builder::ValueRange;
/// Clearable builder value
///
/// This allows a builder function to both accept any value that can [`Into::into`] `T` (like
/// `&str` into `OsStr`) as well as `None` to reset it to the default. This is needed to
/// workaround a limitation where you can't have a function argument that is `impl Into<Option<T>>`
/// where `T` is `impl Into<S>` accept `None` as its type is ambiguous.
///
/// # Example
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// fn common() -> Command {
/// Command::new("cli")
/// .arg(Arg::new("input").short('i').long("input"))
/// }
/// let mut command = common();
/// command.mut_arg("input", |arg| arg.short(None));
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Resettable<T> {
/// Overwrite builder value
Value(T),
/// Reset builder value
Reset,
}
impl<T> Resettable<T> {
pub(crate) fn into_option(self) -> Option<T> {
match self {
Self::Value(t) => Some(t),
Self::Reset => None,
}
}
}
impl<T> From<T> for Resettable<T> {
fn from(other: T) -> Self {
Self::Value(other)
}
}
impl<T> From<Option<T>> for Resettable<T> {
fn from(other: Option<T>) -> Self {
match other {
Some(inner) => Self::Value(inner),
None => Self::Reset,
}
}
}
/// Convert to the intended resettable type
pub trait IntoResettable<T> {
/// Convert to the intended resettable type
fn into_resettable(self) -> Resettable<T>;
}
impl IntoResettable<char> for Option<char> {
fn into_resettable(self) -> Resettable<char> {
match self {
Some(s) => Resettable::Value(s),
None => Resettable::Reset,
}
}
}
impl IntoResettable<usize> for Option<usize> {
fn into_resettable(self) -> Resettable<usize> {
match self {
Some(s) => Resettable::Value(s),
None => Resettable::Reset,
}
}
}
impl IntoResettable<ArgAction> for Option<ArgAction> {
fn into_resettable(self) -> Resettable<ArgAction> {
match self {
Some(s) => Resettable::Value(s),
None => Resettable::Reset,
}
}
}
impl IntoResettable<ValueHint> for Option<ValueHint> {
fn into_resettable(self) -> Resettable<ValueHint> {
match self {
Some(s) => Resettable::Value(s),
None => Resettable::Reset,
}
}
}
impl IntoResettable<ValueParser> for Option<ValueParser> {
fn into_resettable(self) -> Resettable<ValueParser> {
match self {
Some(s) => Resettable::Value(s),
None => Resettable::Reset,
}
}
}
impl IntoResettable<StyledStr> for Option<&'static str> {
fn into_resettable(self) -> Resettable<StyledStr> {
match self {
Some(s) => Resettable::Value(s.into()),
None => Resettable::Reset,
}
}
}
impl IntoResettable<OsStr> for Option<&'static str> {
fn into_resettable(self) -> Resettable<OsStr> {
match self {
Some(s) => Resettable::Value(s.into()),
None => Resettable::Reset,
}
}
}
impl IntoResettable<Str> for Option<&'static str> {
fn into_resettable(self) -> Resettable<Str> {
match self {
Some(s) => Resettable::Value(s.into()),
None => Resettable::Reset,
}
}
}
impl<T> IntoResettable<T> for Resettable<T> {
fn into_resettable(self) -> Resettable<T> {
self
}
}
impl IntoResettable<char> for char {
fn into_resettable(self) -> Resettable<char> {
Resettable::Value(self)
}
}
impl IntoResettable<usize> for usize {
fn into_resettable(self) -> Resettable<usize> {
Resettable::Value(self)
}
}
impl IntoResettable<ArgAction> for ArgAction {
fn into_resettable(self) -> Resettable<ArgAction> {
Resettable::Value(self)
}
}
impl IntoResettable<ValueHint> for ValueHint {
fn into_resettable(self) -> Resettable<ValueHint> {
Resettable::Value(self)
}
}
impl<I: Into<ValueRange>> IntoResettable<ValueRange> for I {
fn into_resettable(self) -> Resettable<ValueRange> {
Resettable::Value(self.into())
}
}
impl<I: Into<ValueParser>> IntoResettable<ValueParser> for I {
fn into_resettable(self) -> Resettable<ValueParser> {
Resettable::Value(self.into())
}
}
impl<I: Into<String>> IntoResettable<String> for I {
fn into_resettable(self) -> Resettable<String> {
Resettable::Value(self.into())
}
}
impl<I: Into<StyledStr>> IntoResettable<StyledStr> for I {
fn into_resettable(self) -> Resettable<StyledStr> {
Resettable::Value(self.into())
}
}
impl<I: Into<OsStr>> IntoResettable<OsStr> for I {
fn into_resettable(self) -> Resettable<OsStr> {
Resettable::Value(self.into())
}
}
impl<I: Into<Str>> IntoResettable<Str> for I {
fn into_resettable(self) -> Resettable<Str> {
Resettable::Value(self.into())
}
}
impl<I: Into<crate::Id>> IntoResettable<crate::Id> for I {
fn into_resettable(self) -> Resettable<crate::Id> {
Resettable::Value(self.into())
}
}

310
vendor/clap_builder/src/builder/str.rs vendored Normal file
View File

@ -0,0 +1,310 @@
/// A UTF-8-encoded fixed string
///
/// **NOTE:** To support dynamic values (i.e. `String`), enable the `string`
/// feature
#[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Str {
name: Inner,
}
impl Str {
#[cfg(feature = "string")]
pub(crate) fn from_string(name: std::string::String) -> Self {
Self {
name: Inner::from_string(name),
}
}
#[cfg(feature = "string")]
pub(crate) fn from_ref(name: &str) -> Self {
Self {
name: Inner::from_ref(name),
}
}
pub(crate) fn from_static_ref(name: &'static str) -> Self {
Self {
name: Inner::from_static_ref(name),
}
}
pub(crate) fn into_inner(self) -> Inner {
self.name
}
/// Get the raw string of the `Str`
pub fn as_str(&self) -> &str {
self.name.as_str()
}
}
impl From<&'_ Str> for Str {
fn from(id: &'_ Str) -> Self {
id.clone()
}
}
#[cfg(feature = "string")]
impl From<std::string::String> for Str {
fn from(name: std::string::String) -> Self {
Self::from_string(name)
}
}
#[cfg(feature = "string")]
impl From<&'_ std::string::String> for Str {
fn from(name: &'_ std::string::String) -> Self {
Self::from_ref(name.as_str())
}
}
impl From<&'static str> for Str {
fn from(name: &'static str) -> Self {
Self::from_static_ref(name)
}
}
impl From<&'_ &'static str> for Str {
fn from(name: &'_ &'static str) -> Self {
Self::from_static_ref(name)
}
}
impl From<Str> for String {
fn from(name: Str) -> Self {
name.name.into_string()
}
}
impl From<Str> for Vec<u8> {
fn from(name: Str) -> Self {
String::from(name).into()
}
}
impl From<Str> for std::ffi::OsString {
fn from(name: Str) -> Self {
String::from(name).into()
}
}
impl From<Str> for std::path::PathBuf {
fn from(name: Str) -> Self {
String::from(name).into()
}
}
impl std::fmt::Display for Str {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.as_str(), f)
}
}
impl std::fmt::Debug for Str {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self.as_str(), f)
}
}
impl std::ops::Deref for Str {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for Str {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for Str {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<std::ffi::OsStr> for Str {
#[inline]
fn as_ref(&self) -> &std::ffi::OsStr {
(**self).as_ref()
}
}
impl AsRef<std::path::Path> for Str {
#[inline]
fn as_ref(&self) -> &std::path::Path {
std::path::Path::new(self)
}
}
impl std::borrow::Borrow<str> for Str {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl PartialEq<str> for Str {
#[inline]
fn eq(&self, other: &str) -> bool {
PartialEq::eq(self.as_str(), other)
}
}
impl PartialEq<Str> for str {
#[inline]
fn eq(&self, other: &Str) -> bool {
PartialEq::eq(self, other.as_str())
}
}
impl PartialEq<&'_ str> for Str {
#[inline]
fn eq(&self, other: &&str) -> bool {
PartialEq::eq(self.as_str(), *other)
}
}
impl PartialEq<Str> for &'_ str {
#[inline]
fn eq(&self, other: &Str) -> bool {
PartialEq::eq(*self, other.as_str())
}
}
impl PartialEq<std::ffi::OsStr> for Str {
#[inline]
fn eq(&self, other: &std::ffi::OsStr) -> bool {
PartialEq::eq(self.as_str(), other)
}
}
impl PartialEq<Str> for std::ffi::OsStr {
#[inline]
fn eq(&self, other: &Str) -> bool {
PartialEq::eq(self, other.as_str())
}
}
impl PartialEq<&'_ std::ffi::OsStr> for Str {
#[inline]
fn eq(&self, other: &&std::ffi::OsStr) -> bool {
PartialEq::eq(self.as_str(), *other)
}
}
impl PartialEq<Str> for &'_ std::ffi::OsStr {
#[inline]
fn eq(&self, other: &Str) -> bool {
PartialEq::eq(*self, other.as_str())
}
}
impl PartialEq<std::string::String> for Str {
#[inline]
fn eq(&self, other: &std::string::String) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
impl PartialEq<Str> for std::string::String {
#[inline]
fn eq(&self, other: &Str) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
#[cfg(feature = "string")]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) enum Inner {
Static(&'static str),
Owned(Box<str>),
}
impl Inner {
pub(crate) fn from_string(name: std::string::String) -> Self {
Self::Owned(name.into_boxed_str())
}
pub(crate) fn from_ref(name: &str) -> Self {
Self::Owned(Box::from(name))
}
pub(crate) fn from_static_ref(name: &'static str) -> Self {
Self::Static(name)
}
pub(crate) fn as_str(&self) -> &str {
match self {
Self::Static(s) => s,
Self::Owned(s) => s.as_ref(),
}
}
pub(crate) fn into_string(self) -> String {
match self {
Self::Static(s) => s.to_owned(),
Self::Owned(s) => s.into(),
}
}
}
}
#[cfg(not(feature = "string"))]
pub(crate) mod inner {
#[derive(Clone)]
pub(crate) struct Inner(pub(crate) &'static str);
impl Inner {
pub(crate) fn from_static_ref(name: &'static str) -> Self {
Self(name)
}
pub(crate) fn as_str(&self) -> &str {
self.0
}
pub(crate) fn into_string(self) -> String {
self.as_str().to_owned()
}
}
}
pub(crate) use inner::Inner;
impl Default for Inner {
fn default() -> Self {
Self::from_static_ref("")
}
}
impl PartialEq for Inner {
fn eq(&self, other: &Inner) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialOrd for Inner {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Inner {
fn cmp(&self, other: &Inner) -> std::cmp::Ordering {
self.as_str().cmp(other.as_str())
}
}
impl Eq for Inner {}
impl std::hash::Hash for Inner {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}

View File

@ -0,0 +1,210 @@
#![cfg_attr(not(feature = "usage"), allow(dead_code))]
/// Terminal-styling container
///
/// Styling may be encoded as [ANSI Escape Code](https://en.wikipedia.org/wiki/ANSI_escape_code)
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// // `cstr!` converts tags to ANSI codes
/// let after_help: &'static str = color_print::cstr!(
/// r#"<bold><underline>Examples</underline></bold>
///
/// <dim>$</dim> <bold>mybin --input file.toml</bold>
/// "#);
///
/// let cmd = clap::Command::new("mybin")
/// .after_help(after_help) // The `&str` gets converted into a `StyledStr`
/// // ...
/// # ;
/// ```
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct StyledStr(String);
impl StyledStr {
/// Create an empty buffer
pub const fn new() -> Self {
Self(String::new())
}
/// Display using [ANSI Escape Code](https://en.wikipedia.org/wiki/ANSI_escape_code) styling
#[cfg(feature = "color")]
pub fn ansi(&self) -> impl std::fmt::Display + '_ {
self.0.as_str()
}
/// May allow the compiler to consolidate the `Drop`s for `msg`, reducing code size compared to
/// `styled.push_str(&msg)`
pub(crate) fn push_string(&mut self, msg: String) {
self.0.push_str(&msg);
}
pub(crate) fn push_str(&mut self, msg: &str) {
self.0.push_str(msg);
}
pub(crate) fn trim_start_lines(&mut self) {
if let Some(pos) = self.0.find('\n') {
let (leading, help) = self.0.split_at(pos + 1);
if leading.trim().is_empty() {
self.0 = help.to_owned()
}
}
}
pub(crate) fn trim_end(&mut self) {
self.0 = self.0.trim_end().to_owned()
}
#[cfg(feature = "help")]
pub(crate) fn replace_newline_var(&mut self) {
self.0 = self.0.replace("{n}", "\n");
}
#[cfg(feature = "help")]
pub(crate) fn indent(&mut self, initial: &str, trailing: &str) {
self.0.insert_str(0, initial);
let mut line_sep = "\n".to_owned();
line_sep.push_str(trailing);
self.0 = self.0.replace('\n', &line_sep);
}
#[cfg(all(not(feature = "wrap_help"), feature = "help"))]
pub(crate) fn wrap(&mut self, _hard_width: usize) {}
#[cfg(feature = "wrap_help")]
pub(crate) fn wrap(&mut self, hard_width: usize) {
let mut new = String::with_capacity(self.0.len());
let mut last = 0;
let mut wrapper = crate::output::textwrap::wrap_algorithms::LineWrapper::new(hard_width);
for content in self.iter_text() {
// Preserve styling
let current = content.as_ptr() as usize - self.0.as_str().as_ptr() as usize;
if last != current {
new.push_str(&self.0.as_str()[last..current]);
}
last = current + content.len();
for (i, line) in content.split_inclusive('\n').enumerate() {
if 0 < i {
// reset char count on newline, skipping the start as we might have carried
// over from a prior block of styled text
wrapper.reset();
}
let line = crate::output::textwrap::word_separators::find_words_ascii_space(line)
.collect::<Vec<_>>();
new.extend(wrapper.wrap(line));
}
}
if last != self.0.len() {
new.push_str(&self.0.as_str()[last..]);
}
new = new.trim_end().to_owned();
self.0 = new;
}
#[inline(never)]
#[cfg(feature = "help")]
pub(crate) fn display_width(&self) -> usize {
let mut width = 0;
for c in self.iter_text() {
width += crate::output::display_width(c);
}
width
}
#[cfg(feature = "help")]
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[cfg(feature = "help")]
pub(crate) fn as_styled_str(&self) -> &str {
&self.0
}
#[cfg(feature = "color")]
pub(crate) fn iter_text(&self) -> impl Iterator<Item = &str> {
anstream::adapter::strip_str(&self.0)
}
#[cfg(not(feature = "color"))]
pub(crate) fn iter_text(&self) -> impl Iterator<Item = &str> {
[self.0.as_str()].into_iter()
}
pub(crate) fn push_styled(&mut self, other: &Self) {
self.0.push_str(&other.0);
}
pub(crate) fn write_to(&self, buffer: &mut dyn std::io::Write) -> std::io::Result<()> {
ok!(buffer.write_all(self.0.as_bytes()));
Ok(())
}
}
impl Default for &'_ StyledStr {
fn default() -> Self {
static DEFAULT: StyledStr = StyledStr::new();
&DEFAULT
}
}
impl From<std::string::String> for StyledStr {
fn from(name: std::string::String) -> Self {
StyledStr(name)
}
}
impl From<&'_ std::string::String> for StyledStr {
fn from(name: &'_ std::string::String) -> Self {
let mut styled = StyledStr::new();
styled.push_str(name);
styled
}
}
impl From<&'static str> for StyledStr {
fn from(name: &'static str) -> Self {
let mut styled = StyledStr::new();
styled.push_str(name);
styled
}
}
impl From<&'_ &'static str> for StyledStr {
fn from(name: &'_ &'static str) -> Self {
StyledStr::from(*name)
}
}
impl std::fmt::Write for StyledStr {
#[inline]
fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
self.0.push_str(s);
Ok(())
}
#[inline]
fn write_char(&mut self, c: char) -> Result<(), std::fmt::Error> {
self.0.push(c);
Ok(())
}
}
/// Color-unaware printing. Never uses coloring.
impl std::fmt::Display for StyledStr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for part in self.iter_text() {
part.fmt(f)?;
}
Ok(())
}
}

View File

@ -0,0 +1,179 @@
//! Terminal [`Styles`] for help and error output
pub use anstyle::*;
/// Terminal styling definitions
///
/// See also [`Command::styles`][crate::Command::styles].
///
/// # Example
///
/// clap v3 styling
/// ```rust
/// # use clap_builder as clap;
/// # use clap::builder::styling::*;
/// let styles = Styles::styled()
/// .header(AnsiColor::Yellow.on_default())
/// .usage(AnsiColor::Green.on_default())
/// .literal(AnsiColor::Green.on_default())
/// .placeholder(AnsiColor::Green.on_default());
/// ```
#[derive(Clone, Debug)]
#[allow(missing_copy_implementations)] // Large enough type that I want an explicit `clone()` for now
pub struct Styles {
header: anstyle::Style,
error: anstyle::Style,
usage: anstyle::Style,
literal: anstyle::Style,
placeholder: anstyle::Style,
valid: anstyle::Style,
invalid: anstyle::Style,
}
impl Styles {
/// No terminal styling
pub const fn plain() -> Self {
Self {
header: anstyle::Style::new(),
error: anstyle::Style::new(),
usage: anstyle::Style::new(),
literal: anstyle::Style::new(),
placeholder: anstyle::Style::new(),
valid: anstyle::Style::new(),
invalid: anstyle::Style::new(),
}
}
/// Default terminal styling
pub const fn styled() -> Self {
#[cfg(feature = "color")]
{
Self {
header: anstyle::Style::new().bold().underline(),
error: anstyle::Style::new()
.fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Red)))
.bold(),
usage: anstyle::Style::new().bold().underline(),
literal: anstyle::Style::new().bold(),
placeholder: anstyle::Style::new(),
valid: anstyle::Style::new()
.fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Green))),
invalid: anstyle::Style::new()
.fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Yellow))),
}
}
#[cfg(not(feature = "color"))]
{
Self::plain()
}
}
/// General Heading style, e.g. [`help_heading`][crate::Arg::help_heading]
#[inline]
pub const fn header(mut self, style: anstyle::Style) -> Self {
self.header = style;
self
}
/// Error heading
#[inline]
pub const fn error(mut self, style: anstyle::Style) -> Self {
self.error = style;
self
}
/// Usage heading
#[inline]
pub const fn usage(mut self, style: anstyle::Style) -> Self {
self.usage = style;
self
}
/// Literal command-line syntax, e.g. `--help`
#[inline]
pub const fn literal(mut self, style: anstyle::Style) -> Self {
self.literal = style;
self
}
/// Descriptions within command-line syntax, e.g. [`value_name`][crate::Arg::value_name]
#[inline]
pub const fn placeholder(mut self, style: anstyle::Style) -> Self {
self.placeholder = style;
self
}
/// Highlight suggested usage
#[inline]
pub const fn valid(mut self, style: anstyle::Style) -> Self {
self.valid = style;
self
}
/// Highlight invalid usage
#[inline]
pub const fn invalid(mut self, style: anstyle::Style) -> Self {
self.invalid = style;
self
}
}
/// Reflection
impl Styles {
/// General Heading style, e.g. [`help_heading`][crate::Arg::help_heading]
#[inline(always)]
pub const fn get_header(&self) -> &anstyle::Style {
&self.header
}
/// Error heading
#[inline(always)]
pub const fn get_error(&self) -> &anstyle::Style {
&self.error
}
/// Usage heading
#[inline(always)]
pub const fn get_usage(&self) -> &anstyle::Style {
&self.usage
}
/// Literal command-line syntax, e.g. `--help`
#[inline(always)]
pub const fn get_literal(&self) -> &anstyle::Style {
&self.literal
}
/// Descriptions within command-line syntax, e.g. [`value_name`][crate::Arg::value_name]
#[inline(always)]
pub const fn get_placeholder(&self) -> &anstyle::Style {
&self.placeholder
}
/// Highlight suggested usage
#[inline(always)]
pub const fn get_valid(&self) -> &anstyle::Style {
&self.valid
}
/// Highlight invalid usage
#[inline(always)]
pub const fn get_invalid(&self) -> &anstyle::Style {
&self.invalid
}
}
impl super::AppTag for Styles {}
impl Default for Styles {
fn default() -> Self {
Self::styled()
}
}
impl Default for &'_ Styles {
fn default() -> Self {
const STYLES: Styles = Styles::styled();
&STYLES
}
}

View File

@ -0,0 +1,56 @@
use crate::Arg;
use crate::Command;
#[test]
fn propagate_version() {
let mut cmd = Command::new("test")
.propagate_version(true)
.version("1.1")
.subcommand(Command::new("sub1"));
cmd._propagate();
assert_eq!(
cmd.get_subcommands().next().unwrap().get_version(),
Some("1.1")
);
}
#[test]
fn global_setting() {
let mut cmd = Command::new("test")
.disable_version_flag(true)
.subcommand(Command::new("subcmd"));
cmd._propagate();
assert!(cmd
.get_subcommands()
.find(|s| s.get_name() == "subcmd")
.unwrap()
.is_disable_version_flag_set());
}
// This test will *fail to compile* if Command is not Send + Sync
#[test]
fn app_send_sync() {
fn foo<T: Send + Sync>(_: T) {}
foo(Command::new("test"))
}
#[test]
fn issue_2090() {
let mut cmd = Command::new("cmd")
.disable_version_flag(true)
.subcommand(Command::new("sub"));
cmd._build_self(false);
assert!(cmd
.get_subcommands()
.next()
.unwrap()
.is_disable_version_flag_set());
}
// This test will *fail to compile* if Arg is not Send + Sync
#[test]
fn arg_send_sync() {
fn foo<T: Send + Sync>(_: T) {}
foo(Arg::new("test"))
}

View File

@ -0,0 +1,90 @@
use std::str::FromStr;
/// Provide shell with hint on how to complete an argument.
///
/// See [Arg::value_hint][crate::Arg::value_hint] to set this on an argument.
///
/// See the `clap_complete` crate for completion script generation.
///
/// Overview of which hints are supported by which shell:
///
/// | Hint | zsh | fish[^1]|
/// | ---------------------- | --- | ------- |
/// | `AnyPath` | Yes | Yes |
/// | `FilePath` | Yes | Yes |
/// | `DirPath` | Yes | Yes |
/// | `ExecutablePath` | Yes | Partial |
/// | `CommandName` | Yes | Yes |
/// | `CommandString` | Yes | Partial |
/// | `CommandWithArguments` | Yes | |
/// | `Username` | Yes | Yes |
/// | `Hostname` | Yes | Yes |
/// | `Url` | Yes | |
/// | `EmailAddress` | Yes | |
///
/// [^1]: fish completions currently only support named arguments (e.g. -o or --opt), not
/// positional arguments.
#[derive(Debug, Default, PartialEq, Eq, Hash, Copy, Clone)]
#[non_exhaustive]
pub enum ValueHint {
/// Default value if hint is not specified. Follows shell default behavior, which is usually
/// auto-completing filenames.
#[default]
Unknown,
/// None of the hints below apply. Disables shell completion for this argument.
Other,
/// Any existing path.
AnyPath,
/// Path to a file.
FilePath,
/// Path to a directory.
DirPath,
/// Path to an executable file.
ExecutablePath,
/// Name of a command, without arguments. May be relative to PATH, or full path to executable.
CommandName,
/// A single string containing a command and its arguments.
CommandString,
/// Capture the remaining arguments as a command name and arguments for that command. This is
/// common when writing shell wrappers that execute anther command, for example `sudo` or `env`.
///
/// This hint is special, the argument must be a positional argument and have
/// [`.num_args(1..)`] and Command must use [`Command::trailing_var_arg(true)`]. The result is that the
/// command line `my_app ls -la /` will be parsed as `["ls", "-la", "/"]` and clap won't try to
/// parse the `-la` argument itself.
///
/// [`Command::trailing_var_arg(true)`]: crate::Command::trailing_var_arg
/// [`.num_args(1..)`]: crate::Arg::num_args()
CommandWithArguments,
/// Name of a local operating system user.
Username,
/// Host name of a computer.
/// Shells usually parse `/etc/hosts` and `.ssh/known_hosts` to complete hostnames.
Hostname,
/// Complete web address.
Url,
/// Email address.
EmailAddress,
}
impl FromStr for ValueHint {
type Err = String;
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
Ok(match &*s.to_ascii_lowercase() {
"unknown" => ValueHint::Unknown,
"other" => ValueHint::Other,
"anypath" => ValueHint::AnyPath,
"filepath" => ValueHint::FilePath,
"dirpath" => ValueHint::DirPath,
"executablepath" => ValueHint::ExecutablePath,
"commandname" => ValueHint::CommandName,
"commandstring" => ValueHint::CommandString,
"commandwitharguments" => ValueHint::CommandWithArguments,
"username" => ValueHint::Username,
"hostname" => ValueHint::Hostname,
"url" => ValueHint::Url,
"emailaddress" => ValueHint::EmailAddress,
_ => return Err(format!("unknown ValueHint: `{s}`")),
})
}
}

File diff suppressed because it is too large Load Diff

361
vendor/clap_builder/src/derive.rs vendored Normal file
View File

@ -0,0 +1,361 @@
//! This module contains traits that are usable with the `#[derive(...)]`
//! macros in `clap_derive`.
use crate::builder::PossibleValue;
use crate::{ArgMatches, Command, Error};
use std::ffi::OsString;
/// Parse command-line arguments into `Self`.
///
/// The primary one-stop-shop trait used to create an instance of a `clap`
/// [`Command`], conduct the parsing, and turn the resulting [`ArgMatches`] back
/// into concrete instance of the user struct.
///
/// This trait is primarily a convenience on top of [`FromArgMatches`] +
/// [`CommandFactory`] which uses those two underlying traits to build the two
/// fundamental functions `parse` which uses the `std::env::args_os` iterator,
/// and `parse_from` which allows the consumer to supply the iterator (along
/// with fallible options for each).
///
/// See also [`Subcommand`] and [`Args`].
///
/// **NOTE:** Deriving requires the `derive` feature flag
pub trait Parser: FromArgMatches + CommandFactory + Sized {
/// Parse from `std::env::args_os()`, exit on error
fn parse() -> Self {
let mut matches = <Self as CommandFactory>::command().get_matches();
let res = <Self as FromArgMatches>::from_arg_matches_mut(&mut matches)
.map_err(format_error::<Self>);
match res {
Ok(s) => s,
Err(e) => {
// Since this is more of a development-time error, we aren't doing as fancy of a quit
// as `get_matches`
e.exit()
}
}
}
/// Parse from `std::env::args_os()`, return Err on error.
fn try_parse() -> Result<Self, Error> {
let mut matches = ok!(<Self as CommandFactory>::command().try_get_matches());
<Self as FromArgMatches>::from_arg_matches_mut(&mut matches).map_err(format_error::<Self>)
}
/// Parse from iterator, exit on error
fn parse_from<I, T>(itr: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut matches = <Self as CommandFactory>::command().get_matches_from(itr);
let res = <Self as FromArgMatches>::from_arg_matches_mut(&mut matches)
.map_err(format_error::<Self>);
match res {
Ok(s) => s,
Err(e) => {
// Since this is more of a development-time error, we aren't doing as fancy of a quit
// as `get_matches_from`
e.exit()
}
}
}
/// Parse from iterator, return Err on error.
fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut matches = ok!(<Self as CommandFactory>::command().try_get_matches_from(itr));
<Self as FromArgMatches>::from_arg_matches_mut(&mut matches).map_err(format_error::<Self>)
}
/// Update from iterator, exit on error
fn update_from<I, T>(&mut self, itr: I)
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut matches = <Self as CommandFactory>::command_for_update().get_matches_from(itr);
let res = <Self as FromArgMatches>::update_from_arg_matches_mut(self, &mut matches)
.map_err(format_error::<Self>);
if let Err(e) = res {
// Since this is more of a development-time error, we aren't doing as fancy of a quit
// as `get_matches_from`
e.exit()
}
}
/// Update from iterator, return Err on error.
fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut matches =
ok!(<Self as CommandFactory>::command_for_update().try_get_matches_from(itr));
<Self as FromArgMatches>::update_from_arg_matches_mut(self, &mut matches)
.map_err(format_error::<Self>)
}
}
/// Create a [`Command`] relevant for a user-defined container.
///
/// Derived as part of [`Parser`].
pub trait CommandFactory: Sized {
/// Build a [`Command`] that can instantiate `Self`.
///
/// See [`FromArgMatches::from_arg_matches_mut`] for instantiating `Self`.
fn command() -> Command;
/// Build a [`Command`] that can update `self`.
///
/// See [`FromArgMatches::update_from_arg_matches_mut`] for updating `self`.
fn command_for_update() -> Command;
}
/// Converts an instance of [`ArgMatches`] to a user-defined container.
///
/// Derived as part of [`Parser`], [`Args`], and [`Subcommand`].
pub trait FromArgMatches: Sized {
/// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
///
/// Motivation: If our application had two CLI options, `--name
/// <STRING>` and the flag `--debug`, we may create a struct as follows:
///
/// ```rust
/// # #[cfg(feature = "derive")] {
/// struct Context {
/// name: String,
/// debug: bool
/// }
/// # }
/// ```
///
/// We then need to convert the `ArgMatches` that `clap` generated into our struct.
/// `from_arg_matches` serves as the equivalent of:
///
/// ```rust
/// # #[cfg(feature = "derive")] {
/// # use clap::ArgMatches;
/// # struct Context {
/// # name: String,
/// # debug: bool
/// # }
/// impl From<ArgMatches> for Context {
/// fn from(m: ArgMatches) -> Self {
/// Context {
/// name: m.get_one::<String>("name").unwrap().clone(),
/// debug: m.get_flag("debug"),
/// }
/// }
/// }
/// # }
/// ```
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error>;
/// Instantiate `Self` from [`ArgMatches`], parsing the arguments as needed.
///
/// Motivation: If our application had two CLI options, `--name
/// <STRING>` and the flag `--debug`, we may create a struct as follows:
///
/// ```rust
/// # #[cfg(feature = "derive")] {
/// struct Context {
/// name: String,
/// debug: bool
/// }
/// # }
/// ```
///
/// We then need to convert the `ArgMatches` that `clap` generated into our struct.
/// `from_arg_matches_mut` serves as the equivalent of:
///
/// ```rust
/// # #[cfg(feature = "derive")] {
/// # use clap::ArgMatches;
/// # struct Context {
/// # name: String,
/// # debug: bool
/// # }
/// impl From<ArgMatches> for Context {
/// fn from(m: ArgMatches) -> Self {
/// Context {
/// name: m.get_one::<String>("name").unwrap().to_string(),
/// debug: m.get_flag("debug"),
/// }
/// }
/// }
/// # }
/// ```
fn from_arg_matches_mut(matches: &mut ArgMatches) -> Result<Self, Error> {
Self::from_arg_matches(matches)
}
/// Assign values from `ArgMatches` to `self`.
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error>;
/// Assign values from `ArgMatches` to `self`.
fn update_from_arg_matches_mut(&mut self, matches: &mut ArgMatches) -> Result<(), Error> {
self.update_from_arg_matches(matches)
}
}
/// Parse a set of arguments into a user-defined container.
///
/// Implementing this trait lets a parent container delegate argument parsing behavior to `Self`.
/// with:
/// - `#[command(flatten)] args: ChildArgs`: Attribute can only be used with struct fields that impl
/// `Args`.
/// - `Variant(ChildArgs)`: No attribute is used with enum variants that impl `Args`.
///
/// **NOTE:** Deriving requires the `derive` feature flag
pub trait Args: FromArgMatches + Sized {
/// Report the [`ArgGroup::id`][crate::ArgGroup::id] for this set of arguments
fn group_id() -> Option<crate::Id> {
None
}
/// Append to [`Command`] so it can instantiate `Self`.
///
/// See also [`CommandFactory`].
fn augment_args(cmd: Command) -> Command;
/// Append to [`Command`] so it can update `self`.
///
/// This is used to implement `#[command(flatten)]`
///
/// See also [`CommandFactory`].
fn augment_args_for_update(cmd: Command) -> Command;
}
/// Parse a sub-command into a user-defined enum.
///
/// Implementing this trait lets a parent container delegate subcommand behavior to `Self`.
/// with:
/// - `#[command(subcommand)] field: SubCmd`: Attribute can be used with either struct fields or enum
/// variants that impl `Subcommand`.
/// - `#[command(flatten)] Variant(SubCmd)`: Attribute can only be used with enum variants that impl
/// `Subcommand`.
///
/// **NOTE:** Deriving requires the `derive` feature flag
pub trait Subcommand: FromArgMatches + Sized {
/// Append to [`Command`] so it can instantiate `Self`.
///
/// See also [`CommandFactory`].
fn augment_subcommands(cmd: Command) -> Command;
/// Append to [`Command`] so it can update `self`.
///
/// This is used to implement `#[command(flatten)]`
///
/// See also [`CommandFactory`].
fn augment_subcommands_for_update(cmd: Command) -> Command;
/// Test whether `Self` can parse a specific subcommand
fn has_subcommand(name: &str) -> bool;
}
/// Parse arguments into enums.
///
/// When deriving [`Parser`], a field whose type implements `ValueEnum` can have the attribute
/// `#[arg(value_enum)]` which will
/// - Call [`EnumValueParser`][crate::builder::EnumValueParser]
/// - Allowing using the `#[arg(default_value_t)]` attribute without implementing `Display`.
///
/// **NOTE:** Deriving requires the `derive` feature flag
pub trait ValueEnum: Sized + Clone {
/// All possible argument values, in display order.
fn value_variants<'a>() -> &'a [Self];
/// Parse an argument into `Self`.
fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
Self::value_variants()
.iter()
.find(|v| {
v.to_possible_value()
.expect("ValueEnum::value_variants contains only values with a corresponding ValueEnum::to_possible_value")
.matches(input, ignore_case)
})
.cloned()
.ok_or_else(|| format!("invalid variant: {input}"))
}
/// The canonical argument value.
///
/// The value is `None` for skipped variants.
fn to_possible_value(&self) -> Option<PossibleValue>;
}
impl<T: Parser> Parser for Box<T> {
fn parse() -> Self {
Box::new(<T as Parser>::parse())
}
fn try_parse() -> Result<Self, Error> {
<T as Parser>::try_parse().map(Box::new)
}
fn parse_from<I, It>(itr: I) -> Self
where
I: IntoIterator<Item = It>,
It: Into<OsString> + Clone,
{
Box::new(<T as Parser>::parse_from(itr))
}
fn try_parse_from<I, It>(itr: I) -> Result<Self, Error>
where
I: IntoIterator<Item = It>,
It: Into<OsString> + Clone,
{
<T as Parser>::try_parse_from(itr).map(Box::new)
}
}
impl<T: CommandFactory> CommandFactory for Box<T> {
fn command<'help>() -> Command {
<T as CommandFactory>::command()
}
fn command_for_update<'help>() -> Command {
<T as CommandFactory>::command_for_update()
}
}
impl<T: FromArgMatches> FromArgMatches for Box<T> {
fn from_arg_matches(matches: &ArgMatches) -> Result<Self, Error> {
<T as FromArgMatches>::from_arg_matches(matches).map(Box::new)
}
fn from_arg_matches_mut(matches: &mut ArgMatches) -> Result<Self, Error> {
<T as FromArgMatches>::from_arg_matches_mut(matches).map(Box::new)
}
fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> {
<T as FromArgMatches>::update_from_arg_matches(self, matches)
}
fn update_from_arg_matches_mut(&mut self, matches: &mut ArgMatches) -> Result<(), Error> {
<T as FromArgMatches>::update_from_arg_matches_mut(self, matches)
}
}
impl<T: Args> Args for Box<T> {
fn augment_args(cmd: Command) -> Command {
<T as Args>::augment_args(cmd)
}
fn augment_args_for_update(cmd: Command) -> Command {
<T as Args>::augment_args_for_update(cmd)
}
}
impl<T: Subcommand> Subcommand for Box<T> {
fn augment_subcommands(cmd: Command) -> Command {
<T as Subcommand>::augment_subcommands(cmd)
}
fn augment_subcommands_for_update(cmd: Command) -> Command {
<T as Subcommand>::augment_subcommands_for_update(cmd)
}
fn has_subcommand(name: &str) -> bool {
<T as Subcommand>::has_subcommand(name)
}
}
fn format_error<I: CommandFactory>(err: crate::Error) -> crate::Error {
let mut cmd = I::command();
err.format(&mut cmd)
}

114
vendor/clap_builder/src/error/context.rs vendored Normal file
View File

@ -0,0 +1,114 @@
/// Semantics for a piece of error information
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[cfg(feature = "error-context")]
pub enum ContextKind {
/// The cause of the error
InvalidSubcommand,
/// The cause of the error
InvalidArg,
/// Existing arguments
PriorArg,
/// Accepted subcommands
ValidSubcommand,
/// Accepted values
ValidValue,
/// Rejected values
InvalidValue,
/// Number of values present
ActualNumValues,
/// Number of allowed values
ExpectedNumValues,
/// Minimum number of allowed values
MinValues,
/// Potential fix for the user
SuggestedCommand,
/// Potential fix for the user
SuggestedSubcommand,
/// Potential fix for the user
SuggestedArg,
/// Potential fix for the user
SuggestedValue,
/// Trailing argument
TrailingArg,
/// Potential fix for the user
Suggested,
/// A usage string
Usage,
/// An opaque message to the user
Custom,
}
impl ContextKind {
/// End-user description of the error case, where relevant
pub fn as_str(self) -> Option<&'static str> {
match self {
Self::InvalidSubcommand => Some("Invalid Subcommand"),
Self::InvalidArg => Some("Invalid Argument"),
Self::PriorArg => Some("Prior Argument"),
Self::ValidSubcommand => Some("Valid Subcommand"),
Self::ValidValue => Some("Valid Value"),
Self::InvalidValue => Some("Invalid Value"),
Self::ActualNumValues => Some("Actual Number of Values"),
Self::ExpectedNumValues => Some("Expected Number of Values"),
Self::MinValues => Some("Minimum Number of Values"),
Self::SuggestedCommand => Some("Suggested Command"),
Self::SuggestedSubcommand => Some("Suggested Subcommand"),
Self::SuggestedArg => Some("Suggested Argument"),
Self::SuggestedValue => Some("Suggested Value"),
Self::TrailingArg => Some("Trailing Argument"),
Self::Suggested => Some("Suggested"),
Self::Usage => None,
Self::Custom => None,
}
}
}
impl std::fmt::Display for ContextKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().unwrap_or_default().fmt(f)
}
}
/// A piece of error information
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[cfg(feature = "error-context")]
pub enum ContextValue {
/// [`ContextKind`] is self-sufficient, no additional information needed
None,
/// A single value
Bool(bool),
/// A single value
String(String),
/// Many values
Strings(Vec<String>),
/// A single value
StyledStr(crate::builder::StyledStr),
/// many value
StyledStrs(Vec<crate::builder::StyledStr>),
/// A single value
Number(isize),
}
impl std::fmt::Display for ContextValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => "".fmt(f),
Self::Bool(v) => v.fmt(f),
Self::String(v) => v.fmt(f),
Self::Strings(v) => v.join(", ").fmt(f),
Self::StyledStr(v) => v.fmt(f),
Self::StyledStrs(v) => {
for (i, v) in v.iter().enumerate() {
if i != 0 {
", ".fmt(f)?;
}
v.fmt(f)?;
}
Ok(())
}
Self::Number(v) => v.fmt(f),
}
}
}

545
vendor/clap_builder/src/error/format.rs vendored Normal file
View File

@ -0,0 +1,545 @@
#![allow(missing_copy_implementations)]
#![allow(missing_debug_implementations)]
#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]
use crate::builder::Command;
use crate::builder::StyledStr;
use crate::builder::Styles;
#[cfg(feature = "error-context")]
use crate::error::ContextKind;
#[cfg(feature = "error-context")]
use crate::error::ContextValue;
use crate::error::ErrorKind;
use crate::output::TAB;
/// Defines how to format an error for displaying to the user
pub trait ErrorFormatter: Sized {
/// Stylize the error for the terminal
fn format_error(error: &crate::error::Error<Self>) -> StyledStr;
}
/// Report [`ErrorKind`]
///
/// No context is included.
///
/// **NOTE:** Consider removing the `error-context` default feature if using this to remove all
/// overhead for [`RichFormatter`].
#[non_exhaustive]
pub struct KindFormatter;
impl ErrorFormatter for KindFormatter {
fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
use std::fmt::Write as _;
let styles = &error.inner.styles;
let mut styled = StyledStr::new();
start_error(&mut styled, styles);
if let Some(msg) = error.kind().as_str() {
styled.push_str(msg);
} else if let Some(source) = error.inner.source.as_ref() {
let _ = write!(styled, "{source}");
} else {
styled.push_str("unknown cause");
}
styled.push_str("\n");
styled
}
}
/// Richly formatted error context
///
/// This follows the [rustc diagnostic style guide](https://rustc-dev-guide.rust-lang.org/diagnostics.html#suggestion-style-guide).
#[non_exhaustive]
#[cfg(feature = "error-context")]
pub struct RichFormatter;
#[cfg(feature = "error-context")]
impl ErrorFormatter for RichFormatter {
fn format_error(error: &crate::error::Error<Self>) -> StyledStr {
use std::fmt::Write as _;
let styles = &error.inner.styles;
let valid = &styles.get_valid();
let mut styled = StyledStr::new();
start_error(&mut styled, styles);
if !write_dynamic_context(error, &mut styled, styles) {
if let Some(msg) = error.kind().as_str() {
styled.push_str(msg);
} else if let Some(source) = error.inner.source.as_ref() {
let _ = write!(styled, "{source}");
} else {
styled.push_str("unknown cause");
}
}
let mut suggested = false;
if let Some(valid) = error.get(ContextKind::SuggestedSubcommand) {
styled.push_str("\n");
if !suggested {
styled.push_str("\n");
suggested = true;
}
did_you_mean(&mut styled, styles, "subcommand", valid);
}
if let Some(valid) = error.get(ContextKind::SuggestedArg) {
styled.push_str("\n");
if !suggested {
styled.push_str("\n");
suggested = true;
}
did_you_mean(&mut styled, styles, "argument", valid);
}
if let Some(valid) = error.get(ContextKind::SuggestedValue) {
styled.push_str("\n");
if !suggested {
styled.push_str("\n");
suggested = true;
}
did_you_mean(&mut styled, styles, "value", valid);
}
let suggestions = error.get(ContextKind::Suggested);
if let Some(ContextValue::StyledStrs(suggestions)) = suggestions {
if !suggested {
styled.push_str("\n");
}
for suggestion in suggestions {
let _ = write!(
styled,
"\n{TAB}{}tip:{} ",
valid.render(),
valid.render_reset()
);
styled.push_styled(suggestion);
}
}
let usage = error.get(ContextKind::Usage);
if let Some(ContextValue::StyledStr(usage)) = usage {
put_usage(&mut styled, usage);
}
try_help(&mut styled, styles, error.inner.help_flag);
styled
}
}
fn start_error(styled: &mut StyledStr, styles: &Styles) {
use std::fmt::Write as _;
let error = &styles.get_error();
let _ = write!(styled, "{}error:{} ", error.render(), error.render_reset());
}
#[must_use]
#[cfg(feature = "error-context")]
fn write_dynamic_context(
error: &crate::error::Error,
styled: &mut StyledStr,
styles: &Styles,
) -> bool {
use std::fmt::Write as _;
let valid = styles.get_valid();
let invalid = styles.get_invalid();
let literal = styles.get_literal();
match error.kind() {
ErrorKind::ArgumentConflict => {
let invalid_arg = error.get(ContextKind::InvalidArg);
let prior_arg = error.get(ContextKind::PriorArg);
if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) =
(invalid_arg, prior_arg)
{
if ContextValue::String(invalid_arg.clone()) == *prior_arg {
let _ = write!(
styled,
"the argument '{}{invalid_arg}{}' cannot be used multiple times",
invalid.render(),
invalid.render_reset()
);
} else {
let _ = write!(
styled,
"the argument '{}{invalid_arg}{}' cannot be used with",
invalid.render(),
invalid.render_reset()
);
match prior_arg {
ContextValue::Strings(values) => {
styled.push_str(":");
for v in values {
let _ = write!(
styled,
"\n{TAB}{}{v}{}",
invalid.render(),
invalid.render_reset()
);
}
}
ContextValue::String(value) => {
let _ = write!(
styled,
" '{}{value}{}'",
invalid.render(),
invalid.render_reset()
);
}
_ => {
styled.push_str(" one or more of the other specified arguments");
}
}
}
true
} else {
false
}
}
ErrorKind::NoEquals => {
let invalid_arg = error.get(ContextKind::InvalidArg);
if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
let _ = write!(
styled,
"equal sign is needed when assigning values to '{}{invalid_arg}{}'",
invalid.render(),
invalid.render_reset()
);
true
} else {
false
}
}
ErrorKind::InvalidValue => {
let invalid_arg = error.get(ContextKind::InvalidArg);
let invalid_value = error.get(ContextKind::InvalidValue);
if let (
Some(ContextValue::String(invalid_arg)),
Some(ContextValue::String(invalid_value)),
) = (invalid_arg, invalid_value)
{
if invalid_value.is_empty() {
let _ = write!(
styled,
"a value is required for '{}{invalid_arg}{}' but none was supplied",
invalid.render(),
invalid.render_reset()
);
} else {
let _ = write!(
styled,
"invalid value '{}{invalid_value}{}' for '{}{invalid_arg}{}'",
invalid.render(),
invalid.render_reset(),
literal.render(),
literal.render_reset()
);
}
let values = error.get(ContextKind::ValidValue);
write_values_list("possible values", styled, valid, values);
true
} else {
false
}
}
ErrorKind::InvalidSubcommand => {
let invalid_sub = error.get(ContextKind::InvalidSubcommand);
if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
let _ = write!(
styled,
"unrecognized subcommand '{}{invalid_sub}{}'",
invalid.render(),
invalid.render_reset()
);
true
} else {
false
}
}
ErrorKind::MissingRequiredArgument => {
let invalid_arg = error.get(ContextKind::InvalidArg);
if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg {
styled.push_str("the following required arguments were not provided:");
for v in invalid_arg {
let _ = write!(
styled,
"\n{TAB}{}{v}{}",
valid.render(),
valid.render_reset()
);
}
true
} else {
false
}
}
ErrorKind::MissingSubcommand => {
let invalid_sub = error.get(ContextKind::InvalidSubcommand);
if let Some(ContextValue::String(invalid_sub)) = invalid_sub {
let _ = write!(
styled,
"'{}{invalid_sub}{}' requires a subcommand but one was not provided",
invalid.render(),
invalid.render_reset()
);
let values = error.get(ContextKind::ValidSubcommand);
write_values_list("subcommands", styled, valid, values);
true
} else {
false
}
}
ErrorKind::InvalidUtf8 => false,
ErrorKind::TooManyValues => {
let invalid_arg = error.get(ContextKind::InvalidArg);
let invalid_value = error.get(ContextKind::InvalidValue);
if let (
Some(ContextValue::String(invalid_arg)),
Some(ContextValue::String(invalid_value)),
) = (invalid_arg, invalid_value)
{
let _ = write!(
styled,
"unexpected value '{}{invalid_value}{}' for '{}{invalid_arg}{}' found; no more were expected",
invalid.render(),
invalid.render_reset(),
literal.render(),
literal.render_reset(),
);
true
} else {
false
}
}
ErrorKind::TooFewValues => {
let invalid_arg = error.get(ContextKind::InvalidArg);
let actual_num_values = error.get(ContextKind::ActualNumValues);
let min_values = error.get(ContextKind::MinValues);
if let (
Some(ContextValue::String(invalid_arg)),
Some(ContextValue::Number(actual_num_values)),
Some(ContextValue::Number(min_values)),
) = (invalid_arg, actual_num_values, min_values)
{
let were_provided = singular_or_plural(*actual_num_values as usize);
let _ = write!(
styled,
"{}{min_values}{} more values required by '{}{invalid_arg}{}'; only {}{actual_num_values}{}{were_provided}",
valid.render(),
valid.render_reset(),
literal.render(),
literal.render_reset(),
invalid.render(),
invalid.render_reset(),
);
true
} else {
false
}
}
ErrorKind::ValueValidation => {
let invalid_arg = error.get(ContextKind::InvalidArg);
let invalid_value = error.get(ContextKind::InvalidValue);
if let (
Some(ContextValue::String(invalid_arg)),
Some(ContextValue::String(invalid_value)),
) = (invalid_arg, invalid_value)
{
let _ = write!(
styled,
"invalid value '{}{invalid_value}{}' for '{}{invalid_arg}{}'",
invalid.render(),
invalid.render_reset(),
literal.render(),
literal.render_reset(),
);
if let Some(source) = error.inner.source.as_deref() {
let _ = write!(styled, ": {source}");
}
true
} else {
false
}
}
ErrorKind::WrongNumberOfValues => {
let invalid_arg = error.get(ContextKind::InvalidArg);
let actual_num_values = error.get(ContextKind::ActualNumValues);
let num_values = error.get(ContextKind::ExpectedNumValues);
if let (
Some(ContextValue::String(invalid_arg)),
Some(ContextValue::Number(actual_num_values)),
Some(ContextValue::Number(num_values)),
) = (invalid_arg, actual_num_values, num_values)
{
let were_provided = singular_or_plural(*actual_num_values as usize);
let _ = write!(
styled,
"{}{num_values}{} values required for '{}{invalid_arg}{}' but {}{actual_num_values}{}{were_provided}",
valid.render(),
valid.render_reset(),
literal.render(),
literal.render_reset(),
invalid.render(),
invalid.render_reset(),
);
true
} else {
false
}
}
ErrorKind::UnknownArgument => {
let invalid_arg = error.get(ContextKind::InvalidArg);
if let Some(ContextValue::String(invalid_arg)) = invalid_arg {
let _ = write!(
styled,
"unexpected argument '{}{invalid_arg}{}' found",
invalid.render(),
invalid.render_reset(),
);
true
} else {
false
}
}
ErrorKind::DisplayHelp
| ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
| ErrorKind::DisplayVersion
| ErrorKind::Io
| ErrorKind::Format => false,
}
}
#[cfg(feature = "error-context")]
fn write_values_list(
list_name: &'static str,
styled: &mut StyledStr,
valid: &anstyle::Style,
possible_values: Option<&ContextValue>,
) {
use std::fmt::Write as _;
if let Some(ContextValue::Strings(possible_values)) = possible_values {
if !possible_values.is_empty() {
let _ = write!(styled, "\n{TAB}[{list_name}: ");
let style = valid.render();
let reset = valid.render_reset();
for (idx, val) in possible_values.iter().enumerate() {
if idx > 0 {
styled.push_str(", ");
}
let _ = write!(styled, "{style}{}{reset}", Escape(val));
}
styled.push_str("]");
}
}
}
pub(crate) fn format_error_message(
message: &str,
styles: &Styles,
cmd: Option<&Command>,
usage: Option<&StyledStr>,
) -> StyledStr {
let mut styled = StyledStr::new();
start_error(&mut styled, styles);
styled.push_str(message);
if let Some(usage) = usage {
put_usage(&mut styled, usage);
}
if let Some(cmd) = cmd {
try_help(&mut styled, styles, get_help_flag(cmd));
}
styled
}
/// Returns the singular or plural form on the verb to be based on the argument's value.
fn singular_or_plural(n: usize) -> &'static str {
if n > 1 {
" were provided"
} else {
" was provided"
}
}
fn put_usage(styled: &mut StyledStr, usage: &StyledStr) {
styled.push_str("\n\n");
styled.push_styled(usage);
}
pub(crate) fn get_help_flag(cmd: &Command) -> Option<&'static str> {
if !cmd.is_disable_help_flag_set() {
Some("--help")
} else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() {
Some("help")
} else {
None
}
}
fn try_help(styled: &mut StyledStr, styles: &Styles, help: Option<&str>) {
if let Some(help) = help {
use std::fmt::Write as _;
let literal = &styles.get_literal();
let _ = write!(
styled,
"\n\nFor more information, try '{}{help}{}'.\n",
literal.render(),
literal.render_reset()
);
} else {
styled.push_str("\n");
}
}
#[cfg(feature = "error-context")]
fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, valid: &ContextValue) {
use std::fmt::Write as _;
let _ = write!(
styled,
"{TAB}{}tip:{}",
styles.get_valid().render(),
styles.get_valid().render_reset()
);
if let ContextValue::String(valid) = valid {
let _ = write!(
styled,
" a similar {context} exists: '{}{valid}{}'",
styles.get_valid().render(),
styles.get_valid().render_reset()
);
} else if let ContextValue::Strings(valid) = valid {
if valid.len() == 1 {
let _ = write!(styled, " a similar {context} exists: ",);
} else {
let _ = write!(styled, " some similar {context}s exist: ",);
}
for (i, valid) in valid.iter().enumerate() {
if i != 0 {
styled.push_str(", ");
}
let _ = write!(
styled,
"'{}{valid}{}'",
styles.get_valid().render(),
styles.get_valid().render_reset()
);
}
}
}
struct Escape<'s>(&'s str);
impl<'s> std::fmt::Display for Escape<'s> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.0.contains(char::is_whitespace) {
std::fmt::Debug::fmt(self.0, f)
} else {
self.0.fmt(f)
}
}
}

366
vendor/clap_builder/src/error/kind.rs vendored Normal file
View File

@ -0,0 +1,366 @@
/// Command line argument parser kind of error
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// Occurs when an [`Arg`][crate::Arg] has a set of possible values,
/// and the user provides a value which isn't in that set.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .arg(Arg::new("speed")
/// .value_parser(["fast", "slow"]))
/// .try_get_matches_from(vec!["prog", "other"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
/// ```
InvalidValue,
/// Occurs when a user provides a flag, option, argument or subcommand which isn't defined.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .arg(arg!(--flag "some flag"))
/// .try_get_matches_from(vec!["prog", "--other"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnknownArgument);
/// ```
UnknownArgument,
/// Occurs when the user provides an unrecognized [`Subcommand`] which meets the threshold for
/// being similar enough to an existing subcommand.
/// If it doesn't meet the threshold, or the 'suggestions' feature is disabled,
/// the more general [`UnknownArgument`] error is returned.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "suggestions")] {
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, };
/// let result = Command::new("prog")
/// .subcommand(Command::new("config")
/// .about("Used for configuration")
/// .arg(Arg::new("config_file")
/// .help("The configuration file to use")))
/// .try_get_matches_from(vec!["prog", "confi"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
/// # }
/// ```
///
/// [`Subcommand`]: crate::Subcommand
/// [`UnknownArgument`]: ErrorKind::UnknownArgument
InvalidSubcommand,
/// Occurs when the user doesn't use equals for an option that requires equal
/// sign to provide values.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
/// let res = Command::new("prog")
/// .arg(Arg::new("color")
/// .action(ArgAction::Set)
/// .require_equals(true)
/// .long("color"))
/// .try_get_matches_from(vec!["prog", "--color", "red"]);
/// assert!(res.is_err());
/// assert_eq!(res.unwrap_err().kind(), ErrorKind::NoEquals);
/// ```
NoEquals,
/// Occurs when the user provides a value for an argument with a custom validation and the
/// value fails that validation.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, value_parser};
/// fn is_numeric(val: &str) -> Result<(), String> {
/// match val.parse::<i64>() {
/// Ok(..) => Ok(()),
/// Err(..) => Err(String::from("value wasn't a number!")),
/// }
/// }
///
/// let result = Command::new("prog")
/// .arg(Arg::new("num")
/// .value_parser(value_parser!(u8)))
/// .try_get_matches_from(vec!["prog", "NotANumber"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::ValueValidation);
/// ```
ValueValidation,
/// Occurs when a user provides more values for an argument than were defined by setting
/// [`Arg::num_args`].
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .arg(Arg::new("arg")
/// .num_args(1..=2))
/// .try_get_matches_from(vec!["prog", "too", "many", "values"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyValues);
/// ```
/// [`Arg::num_args`]: crate::Arg::num_args()
TooManyValues,
/// Occurs when the user provides fewer values for an argument than were defined by setting
/// [`Arg::num_args`].
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .arg(Arg::new("some_opt")
/// .long("opt")
/// .num_args(3..))
/// .try_get_matches_from(vec!["prog", "--opt", "too", "few"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooFewValues);
/// ```
/// [`Arg::num_args`]: crate::Arg::num_args()
TooFewValues,
/// Occurs when the user provides a different number of values for an argument than what's
/// been defined by setting [`Arg::num_args`] or than was implicitly set by
/// [`Arg::value_names`].
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
/// let result = Command::new("prog")
/// .arg(Arg::new("some_opt")
/// .long("opt")
/// .action(ArgAction::Set)
/// .num_args(2))
/// .try_get_matches_from(vec!["prog", "--opt", "wrong"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::WrongNumberOfValues);
/// ```
///
/// [`Arg::num_args`]: crate::Arg::num_args()
/// [`Arg::value_names`]: crate::Arg::value_names()
WrongNumberOfValues,
/// Occurs when the user provides two values which conflict with each other and can't be used
/// together.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
/// let result = Command::new("prog")
/// .arg(Arg::new("debug")
/// .long("debug")
/// .action(ArgAction::SetTrue)
/// .conflicts_with("color"))
/// .arg(Arg::new("color")
/// .long("color")
/// .action(ArgAction::SetTrue))
/// .try_get_matches_from(vec!["prog", "--debug", "--color"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::ArgumentConflict);
/// ```
ArgumentConflict,
/// Occurs when the user does not provide one or more required arguments.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .arg(Arg::new("debug")
/// .required(true))
/// .try_get_matches_from(vec!["prog"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
/// ```
MissingRequiredArgument,
/// Occurs when a subcommand is required (as defined by [`Command::subcommand_required`]),
/// but the user does not provide one.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, error::ErrorKind};
/// let err = Command::new("prog")
/// .subcommand_required(true)
/// .subcommand(Command::new("test"))
/// .try_get_matches_from(vec![
/// "myprog",
/// ]);
/// assert!(err.is_err());
/// assert_eq!(err.unwrap_err().kind(), ErrorKind::MissingSubcommand);
/// # ;
/// ```
///
/// [`Command::subcommand_required`]: crate::Command::subcommand_required
MissingSubcommand,
/// Occurs when the user provides a value containing invalid UTF-8.
///
/// To allow arbitrary data
/// - Set [`Arg::value_parser(value_parser!(OsString))`] for argument values
/// - Set [`Command::external_subcommand_value_parser`] for external-subcommand
/// values
///
/// # Platform Specific
///
/// Non-Windows platforms only (such as Linux, Unix, OSX, etc.)
///
/// # Examples
///
/// ```rust
/// # #[cfg(unix)] {
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, ArgAction};
/// # use std::os::unix::ffi::OsStringExt;
/// # use std::ffi::OsString;
/// let result = Command::new("prog")
/// .arg(Arg::new("utf8")
/// .short('u')
/// .action(ArgAction::Set))
/// .try_get_matches_from(vec![OsString::from("myprog"),
/// OsString::from("-u"),
/// OsString::from_vec(vec![0xE9])]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidUtf8);
/// # }
/// ```
///
/// [`Arg::allow_invalid_utf8`]: crate::Arg::allow_invalid_utf8
/// [`Command::external_subcommand_value_parser`]: crate::Command::external_subcommand_value_parser
InvalidUtf8,
/// Not a true "error" as it means `--help` or similar was used.
/// The help message will be sent to `stdout`.
///
/// **Note**: If the help is displayed due to an error (such as missing subcommands) it will
/// be sent to `stderr` instead of `stdout`.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "help")] {
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .try_get_matches_from(vec!["prog", "--help"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayHelp);
/// # }
/// ```
DisplayHelp,
/// Occurs when either an argument or a [`Subcommand`] is required, as defined by
/// [`Command::arg_required_else_help`] , but the user did not provide
/// one.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind, };
/// let result = Command::new("prog")
/// .arg_required_else_help(true)
/// .subcommand(Command::new("config")
/// .about("Used for configuration")
/// .arg(Arg::new("config_file")
/// .help("The configuration file to use")))
/// .try_get_matches_from(vec!["prog"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand);
/// ```
///
/// [`Subcommand`]: crate::Subcommand
/// [`Command::arg_required_else_help`]: crate::Command::arg_required_else_help
DisplayHelpOnMissingArgumentOrSubcommand,
/// Not a true "error" as it means `--version` or similar was used.
/// The message will be sent to `stdout`.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, error::ErrorKind};
/// let result = Command::new("prog")
/// .version("3.0")
/// .try_get_matches_from(vec!["prog", "--version"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayVersion);
/// ```
DisplayVersion,
/// Represents an [I/O error].
/// Can occur when writing to `stderr` or `stdout` or reading a configuration file.
///
/// [I/O error]: std::io::Error
Io,
/// Represents a [Format error] (which is a part of [`Display`]).
/// Typically caused by writing to `stderr` or `stdout`.
///
/// [`Display`]: std::fmt::Display
/// [Format error]: std::fmt::Error
Format,
}
impl ErrorKind {
/// End-user description of the error case, where relevant
pub fn as_str(self) -> Option<&'static str> {
match self {
Self::InvalidValue => Some("one of the values isn't valid for an argument"),
Self::UnknownArgument => Some("unexpected argument found"),
Self::InvalidSubcommand => Some("unrecognized subcommand"),
Self::NoEquals => Some("equal is needed when assigning values to one of the arguments"),
Self::ValueValidation => Some("invalid value for one of the arguments"),
Self::TooManyValues => Some("unexpected value for an argument found"),
Self::TooFewValues => Some("more values required for an argument"),
Self::WrongNumberOfValues => Some("too many or too few values for an argument"),
Self::ArgumentConflict => {
Some("an argument cannot be used with one or more of the other specified arguments")
}
Self::MissingRequiredArgument => {
Some("one or more required arguments were not provided")
}
Self::MissingSubcommand => Some("a subcommand is required but one was not provided"),
Self::InvalidUtf8 => Some("invalid UTF-8 was detected in one or more arguments"),
Self::DisplayHelp => None,
Self::DisplayHelpOnMissingArgumentOrSubcommand => None,
Self::DisplayVersion => None,
Self::Io => None,
Self::Format => None,
}
}
}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().unwrap_or_default().fmt(f)
}
}

923
vendor/clap_builder/src/error/mod.rs vendored Normal file
View File

@ -0,0 +1,923 @@
//! Error reporting
#![cfg_attr(not(feature = "error-context"), allow(dead_code))]
#![cfg_attr(not(feature = "error-context"), allow(unused_imports))]
#![cfg_attr(not(feature = "error-context"), allow(unused_variables))]
#![cfg_attr(not(feature = "error-context"), allow(unused_mut))]
#![cfg_attr(not(feature = "error-context"), allow(clippy::let_and_return))]
// Std
use std::{
borrow::Cow,
convert::From,
error,
fmt::{self, Debug, Display, Formatter},
io::{self},
result::Result as StdResult,
};
// Internal
use crate::builder::StyledStr;
use crate::builder::Styles;
use crate::output::fmt::Colorizer;
use crate::output::fmt::Stream;
use crate::parser::features::suggestions;
use crate::util::FlatMap;
use crate::util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE};
use crate::Command;
#[cfg(feature = "error-context")]
mod context;
mod format;
mod kind;
pub use format::ErrorFormatter;
pub use format::KindFormatter;
pub use kind::ErrorKind;
#[cfg(feature = "error-context")]
pub use context::ContextKind;
#[cfg(feature = "error-context")]
pub use context::ContextValue;
#[cfg(feature = "error-context")]
pub use format::RichFormatter;
#[cfg(not(feature = "error-context"))]
pub use KindFormatter as DefaultFormatter;
#[cfg(feature = "error-context")]
pub use RichFormatter as DefaultFormatter;
/// Short hand for [`Result`] type
///
/// [`Result`]: std::result::Result
pub type Result<T, E = Error> = StdResult<T, E>;
/// Command Line Argument Parser Error
///
/// See [`Command::error`] to create an error.
///
/// [`Command::error`]: crate::Command::error
pub struct Error<F: ErrorFormatter = DefaultFormatter> {
inner: Box<ErrorInner>,
phantom: std::marker::PhantomData<F>,
}
#[derive(Debug)]
struct ErrorInner {
kind: ErrorKind,
#[cfg(feature = "error-context")]
context: FlatMap<ContextKind, ContextValue>,
message: Option<Message>,
source: Option<Box<dyn error::Error + Send + Sync>>,
help_flag: Option<&'static str>,
styles: Styles,
color_when: ColorChoice,
color_help_when: ColorChoice,
backtrace: Option<Backtrace>,
}
impl<F: ErrorFormatter> Error<F> {
/// Create an unformatted error
///
/// This is for you need to pass the error up to
/// a place that has access to the `Command` at which point you can call [`Error::format`].
///
/// Prefer [`Command::error`] for generating errors.
///
/// [`Command::error`]: crate::Command::error
pub fn raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self {
Self::new(kind).set_message(message.to_string())
}
/// Format the existing message with the Command's context
#[must_use]
pub fn format(mut self, cmd: &mut Command) -> Self {
cmd._build_self(false);
let usage = cmd.render_usage_();
if let Some(message) = self.inner.message.as_mut() {
message.format(cmd, usage);
}
self.with_cmd(cmd)
}
/// Create an error with a pre-defined message
///
/// See also
/// - [`Error::insert`]
/// - [`Error::with_cmd`]
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "error-context")] {
/// # use clap_builder as clap;
/// # use clap::error::ErrorKind;
/// # use clap::error::ContextKind;
/// # use clap::error::ContextValue;
///
/// let cmd = clap::Command::new("prog");
///
/// let mut err = clap::Error::new(ErrorKind::ValueValidation)
/// .with_cmd(&cmd);
/// err.insert(ContextKind::InvalidArg, ContextValue::String("--foo".to_owned()));
/// err.insert(ContextKind::InvalidValue, ContextValue::String("bar".to_owned()));
///
/// err.print();
/// # }
/// ```
pub fn new(kind: ErrorKind) -> Self {
Self {
inner: Box::new(ErrorInner {
kind,
#[cfg(feature = "error-context")]
context: FlatMap::new(),
message: None,
source: None,
help_flag: None,
styles: Styles::plain(),
color_when: ColorChoice::Never,
color_help_when: ColorChoice::Never,
backtrace: Backtrace::new(),
}),
phantom: Default::default(),
}
}
/// Apply [`Command`]'s formatting to the error
///
/// Generally, this is used with [`Error::new`]
pub fn with_cmd(self, cmd: &Command) -> Self {
self.set_styles(cmd.get_styles().clone())
.set_color(cmd.get_color())
.set_colored_help(cmd.color_help())
.set_help_flag(format::get_help_flag(cmd))
}
/// Apply an alternative formatter to the error
///
/// # Example
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// # use clap::Arg;
/// # use clap::error::KindFormatter;
/// let cmd = Command::new("foo")
/// .arg(Arg::new("input").required(true));
/// let matches = cmd
/// .try_get_matches_from(["foo", "input.txt"])
/// .map_err(|e| e.apply::<KindFormatter>())
/// .unwrap_or_else(|e| e.exit());
/// ```
pub fn apply<EF: ErrorFormatter>(self) -> Error<EF> {
Error {
inner: self.inner,
phantom: Default::default(),
}
}
/// Type of error for programmatic processing
pub fn kind(&self) -> ErrorKind {
self.inner.kind
}
/// Additional information to further qualify the error
#[cfg(feature = "error-context")]
pub fn context(&self) -> impl Iterator<Item = (ContextKind, &ContextValue)> {
self.inner.context.iter().map(|(k, v)| (*k, v))
}
/// Lookup a piece of context
#[inline(never)]
#[cfg(feature = "error-context")]
pub fn get(&self, kind: ContextKind) -> Option<&ContextValue> {
self.inner.context.get(&kind)
}
/// Insert a piece of context
#[inline(never)]
#[cfg(feature = "error-context")]
pub fn insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue> {
self.inner.context.insert(kind, value)
}
/// Should the message be written to `stdout` or not?
#[inline]
pub fn use_stderr(&self) -> bool {
self.stream() == Stream::Stderr
}
pub(crate) fn stream(&self) -> Stream {
match self.kind() {
ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout,
_ => Stream::Stderr,
}
}
/// Returns the exit code that `.exit` will exit the process with.
///
/// When the error's kind would print to `stderr` this returns `2`,
/// else it returns `0`.
pub fn exit_code(&self) -> i32 {
if self.use_stderr() {
USAGE_CODE
} else {
SUCCESS_CODE
}
}
/// Prints the error and exits.
///
/// Depending on the error kind, this either prints to `stderr` and exits with a status of `2`
/// or prints to `stdout` and exits with a status of `0`.
pub fn exit(&self) -> ! {
// Swallow broken pipe errors
let _ = self.print();
safe_exit(self.exit_code())
}
/// Prints formatted and colored error to `stdout` or `stderr` according to its error kind
///
/// # Example
/// ```no_run
/// # use clap_builder as clap;
/// use clap::Command;
///
/// match Command::new("Command").try_get_matches() {
/// Ok(matches) => {
/// // do_something
/// },
/// Err(err) => {
/// err.print().expect("Error writing Error");
/// // do_something
/// },
/// };
/// ```
pub fn print(&self) -> io::Result<()> {
let style = self.formatted();
let color_when = if matches!(
self.kind(),
ErrorKind::DisplayHelp | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
) {
self.inner.color_help_when
} else {
self.inner.color_when
};
let c = Colorizer::new(self.stream(), color_when).with_content(style.into_owned());
c.print()
}
/// Render the error message to a [`StyledStr`].
///
/// # Example
/// ```no_run
/// # use clap_builder as clap;
/// use clap::Command;
///
/// match Command::new("Command").try_get_matches() {
/// Ok(matches) => {
/// // do_something
/// },
/// Err(err) => {
/// let err = err.render();
/// println!("{err}");
/// // do_something
/// },
/// };
/// ```
pub fn render(&self) -> StyledStr {
self.formatted().into_owned()
}
#[inline(never)]
fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self {
Self::new(kind).set_message(styled).with_cmd(cmd)
}
pub(crate) fn set_message(mut self, message: impl Into<Message>) -> Self {
self.inner.message = Some(message.into());
self
}
pub(crate) fn set_source(mut self, source: Box<dyn error::Error + Send + Sync>) -> Self {
self.inner.source = Some(source);
self
}
pub(crate) fn set_styles(mut self, styles: Styles) -> Self {
self.inner.styles = styles;
self
}
pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self {
self.inner.color_when = color_when;
self
}
pub(crate) fn set_colored_help(mut self, color_help_when: ColorChoice) -> Self {
self.inner.color_help_when = color_help_when;
self
}
pub(crate) fn set_help_flag(mut self, help_flag: Option<&'static str>) -> Self {
self.inner.help_flag = help_flag;
self
}
/// Does not verify if `ContextKind` is already present
#[inline(never)]
#[cfg(feature = "error-context")]
pub(crate) fn insert_context_unchecked(
mut self,
kind: ContextKind,
value: ContextValue,
) -> Self {
self.inner.context.insert_unchecked(kind, value);
self
}
/// Does not verify if `ContextKind` is already present
#[inline(never)]
#[cfg(feature = "error-context")]
pub(crate) fn extend_context_unchecked<const N: usize>(
mut self,
context: [(ContextKind, ContextValue); N],
) -> Self {
self.inner.context.extend_unchecked(context);
self
}
pub(crate) fn display_help(cmd: &Command, styled: StyledStr) -> Self {
Self::for_app(ErrorKind::DisplayHelp, cmd, styled)
}
pub(crate) fn display_help_error(cmd: &Command, styled: StyledStr) -> Self {
Self::for_app(
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
cmd,
styled,
)
}
pub(crate) fn display_version(cmd: &Command, styled: StyledStr) -> Self {
Self::for_app(ErrorKind::DisplayVersion, cmd, styled)
}
pub(crate) fn argument_conflict(
cmd: &Command,
arg: String,
mut others: Vec<String>,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::ArgumentConflict).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
let others = match others.len() {
0 => ContextValue::None,
1 => ContextValue::String(others.pop().unwrap()),
_ => ContextValue::Strings(others),
};
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(ContextKind::PriorArg, others),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn empty_value(cmd: &Command, good_vals: &[String], arg: String) -> Self {
Self::invalid_value(cmd, "".to_owned(), good_vals, arg)
}
pub(crate) fn no_equals(cmd: &Command, arg: String, usage: Option<StyledStr>) -> Self {
let mut err = Self::new(ErrorKind::NoEquals).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err
.extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn invalid_value(
cmd: &Command,
bad_val: String,
good_vals: &[String],
arg: String,
) -> Self {
let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
let mut err = Self::new(ErrorKind::InvalidValue).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(ContextKind::InvalidValue, ContextValue::String(bad_val)),
(
ContextKind::ValidValue,
ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()),
),
]);
if let Some(suggestion) = suggestion {
err = err.insert_context_unchecked(
ContextKind::SuggestedValue,
ContextValue::String(suggestion),
);
}
}
err
}
pub(crate) fn invalid_subcommand(
cmd: &Command,
subcmd: String,
did_you_mean: Vec<String>,
name: String,
suggested_trailing_arg: bool,
usage: Option<StyledStr>,
) -> Self {
use std::fmt::Write as _;
let styles = cmd.get_styles();
let invalid = &styles.get_invalid();
let valid = &styles.get_valid();
let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
let mut suggestions = vec![];
if suggested_trailing_arg {
let mut styled_suggestion = StyledStr::new();
let _ = write!(
styled_suggestion,
"to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'",
invalid.render(),
invalid.render_reset(),
valid.render(),
valid.render_reset()
);
suggestions.push(styled_suggestion);
}
err = err.extend_context_unchecked([
(ContextKind::InvalidSubcommand, ContextValue::String(subcmd)),
(
ContextKind::SuggestedSubcommand,
ContextValue::Strings(did_you_mean),
),
(
ContextKind::Suggested,
ContextValue::StyledStrs(suggestions),
),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn unrecognized_subcommand(
cmd: &Command,
subcmd: String,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::InvalidSubcommand).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([(
ContextKind::InvalidSubcommand,
ContextValue::String(subcmd),
)]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn missing_required_argument(
cmd: &Command,
required: Vec<String>,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::MissingRequiredArgument).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([(
ContextKind::InvalidArg,
ContextValue::Strings(required),
)]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn missing_subcommand(
cmd: &Command,
parent: String,
available: Vec<String>,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::MissingSubcommand).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([
(ContextKind::InvalidSubcommand, ContextValue::String(parent)),
(
ContextKind::ValidSubcommand,
ContextValue::Strings(available),
),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn invalid_utf8(cmd: &Command, usage: Option<StyledStr>) -> Self {
let mut err = Self::new(ErrorKind::InvalidUtf8).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn too_many_values(
cmd: &Command,
val: String,
arg: String,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::TooManyValues).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(ContextKind::InvalidValue, ContextValue::String(val)),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn too_few_values(
cmd: &Command,
arg: String,
min_vals: usize,
curr_vals: usize,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::TooFewValues).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(
ContextKind::MinValues,
ContextValue::Number(min_vals as isize),
),
(
ContextKind::ActualNumValues,
ContextValue::Number(curr_vals as isize),
),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn value_validation(
arg: String,
val: String,
err: Box<dyn error::Error + Send + Sync>,
) -> Self {
let mut err = Self::new(ErrorKind::ValueValidation).set_source(err);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(ContextKind::InvalidValue, ContextValue::String(val)),
]);
}
err
}
pub(crate) fn wrong_number_of_values(
cmd: &Command,
arg: String,
num_vals: usize,
curr_vals: usize,
usage: Option<StyledStr>,
) -> Self {
let mut err = Self::new(ErrorKind::WrongNumberOfValues).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(
ContextKind::ExpectedNumValues,
ContextValue::Number(num_vals as isize),
),
(
ContextKind::ActualNumValues,
ContextValue::Number(curr_vals as isize),
),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
pub(crate) fn unknown_argument(
cmd: &Command,
arg: String,
did_you_mean: Option<(String, Option<String>)>,
suggested_trailing_arg: bool,
usage: Option<StyledStr>,
) -> Self {
use std::fmt::Write as _;
let styles = cmd.get_styles();
let invalid = &styles.get_invalid();
let valid = &styles.get_valid();
let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
let mut suggestions = vec![];
if suggested_trailing_arg {
let mut styled_suggestion = StyledStr::new();
let _ = write!(
styled_suggestion,
"to pass '{}{arg}{}' as a value, use '{}-- {arg}{}'",
invalid.render(),
invalid.render_reset(),
valid.render(),
valid.render_reset()
);
suggestions.push(styled_suggestion);
}
err = err
.extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
match did_you_mean {
Some((flag, Some(sub))) => {
let mut styled_suggestion = StyledStr::new();
let _ = write!(
styled_suggestion,
"'{}{sub} {flag}{}' exists",
valid.render(),
valid.render_reset()
);
suggestions.push(styled_suggestion);
}
Some((flag, None)) => {
err = err.insert_context_unchecked(
ContextKind::SuggestedArg,
ContextValue::String(flag),
);
}
None => {}
}
if !suggestions.is_empty() {
err = err.insert_context_unchecked(
ContextKind::Suggested,
ContextValue::StyledStrs(suggestions),
);
}
}
err
}
pub(crate) fn unnecessary_double_dash(
cmd: &Command,
arg: String,
usage: Option<StyledStr>,
) -> Self {
use std::fmt::Write as _;
let styles = cmd.get_styles();
let invalid = &styles.get_invalid();
let valid = &styles.get_valid();
let mut err = Self::new(ErrorKind::UnknownArgument).with_cmd(cmd);
#[cfg(feature = "error-context")]
{
let mut styled_suggestion = StyledStr::new();
let _ = write!(
styled_suggestion,
"subcommand '{}{arg}{}' exists; to use it, remove the '{}--{}' before it",
valid.render(),
valid.render_reset(),
invalid.render(),
invalid.render_reset()
);
err = err.extend_context_unchecked([
(ContextKind::InvalidArg, ContextValue::String(arg)),
(
ContextKind::Suggested,
ContextValue::StyledStrs(vec![styled_suggestion]),
),
]);
if let Some(usage) = usage {
err = err
.insert_context_unchecked(ContextKind::Usage, ContextValue::StyledStr(usage));
}
}
err
}
fn formatted(&self) -> Cow<'_, StyledStr> {
if let Some(message) = self.inner.message.as_ref() {
message.formatted(&self.inner.styles)
} else {
let styled = F::format_error(self);
Cow::Owned(styled)
}
}
}
impl<F: ErrorFormatter> From<io::Error> for Error<F> {
fn from(e: io::Error) -> Self {
Error::raw(ErrorKind::Io, e)
}
}
impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
fn from(e: fmt::Error) -> Self {
Error::raw(ErrorKind::Format, e)
}
}
impl<F: ErrorFormatter> std::fmt::Debug for Error<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.inner.fmt(f)
}
}
impl<F: ErrorFormatter> error::Error for Error<F> {
#[allow(trivial_casts)]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.inner.source.as_ref().map(|e| e.as_ref() as _)
}
}
impl<F: ErrorFormatter> Display for Error<F> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
// Assuming `self.message` already has a trailing newline, from `try_help` or similar
ok!(write!(f, "{}", self.formatted()));
if let Some(backtrace) = self.inner.backtrace.as_ref() {
ok!(writeln!(f));
ok!(writeln!(f, "Backtrace:"));
ok!(writeln!(f, "{backtrace}"));
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub(crate) enum Message {
Raw(String),
Formatted(StyledStr),
}
impl Message {
fn format(&mut self, cmd: &Command, usage: Option<StyledStr>) {
match self {
Message::Raw(s) => {
let mut message = String::new();
std::mem::swap(s, &mut message);
let styled = format::format_error_message(
&message,
cmd.get_styles(),
Some(cmd),
usage.as_ref(),
);
*self = Self::Formatted(styled);
}
Message::Formatted(_) => {}
}
}
fn formatted(&self, styles: &Styles) -> Cow<StyledStr> {
match self {
Message::Raw(s) => {
let styled = format::format_error_message(s, styles, None, None);
Cow::Owned(styled)
}
Message::Formatted(s) => Cow::Borrowed(s),
}
}
}
impl From<String> for Message {
fn from(inner: String) -> Self {
Self::Raw(inner)
}
}
impl From<StyledStr> for Message {
fn from(inner: StyledStr) -> Self {
Self::Formatted(inner)
}
}
#[cfg(feature = "debug")]
#[derive(Debug)]
struct Backtrace(backtrace::Backtrace);
#[cfg(feature = "debug")]
impl Backtrace {
fn new() -> Option<Self> {
Some(Self(backtrace::Backtrace::new()))
}
}
#[cfg(feature = "debug")]
impl Display for Backtrace {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
// `backtrace::Backtrace` uses `Debug` instead of `Display`
write!(f, "{:?}", self.0)
}
}
#[cfg(not(feature = "debug"))]
#[derive(Debug)]
struct Backtrace;
#[cfg(not(feature = "debug"))]
impl Backtrace {
fn new() -> Option<Self> {
None
}
}
#[cfg(not(feature = "debug"))]
impl Display for Backtrace {
fn fmt(&self, _: &mut Formatter) -> fmt::Result {
Ok(())
}
}
#[test]
fn check_auto_traits() {
static_assertions::assert_impl_all!(Error: Send, Sync, Unpin);
}

66
vendor/clap_builder/src/lib.rs vendored Normal file
View File

@ -0,0 +1,66 @@
// Copyright ⓒ 2015-2016 Kevin B. Knapp and [`clap-rs` contributors](https://github.com/clap-rs/clap/graphs/contributors).
// Licensed under the MIT license
// (see LICENSE or <http://opensource.org/licenses/MIT>) All files in the project carrying such
// notice may not be copied, modified, or distributed except according to those terms.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/clap-rs/clap/master/assets/clap.png")]
#![warn(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
unused_allocation,
trivial_numeric_casts,
clippy::single_char_pattern
)]
#![forbid(unsafe_code)]
// Wanting consistency in our calls
#![allow(clippy::write_with_newline)]
// Gets in the way of logging
#![allow(clippy::let_and_return)]
// HACK https://github.com/rust-lang/rust-clippy/issues/7290
#![allow(clippy::single_component_path_imports)]
#![allow(clippy::branches_sharing_code)]
// Doesn't allow for debug statements, etc to be unique
#![allow(clippy::if_same_then_else)]
// Breaks up parallelism that clarifies intent
#![allow(clippy::collapsible_else_if)]
#[cfg(not(feature = "std"))]
compile_error!("`std` feature is currently required to build `clap`");
pub use crate::builder::ArgAction;
pub use crate::builder::Command;
pub use crate::builder::ValueHint;
pub use crate::builder::{Arg, ArgGroup};
pub use crate::parser::ArgMatches;
pub use crate::util::color::ColorChoice;
pub use crate::util::Id;
/// Command Line Argument Parser Error
///
/// See [`Command::error`] to create an error.
///
/// [`Command::error`]: crate::Command::error
pub type Error = crate::error::Error<crate::error::DefaultFormatter>;
pub use crate::derive::{Args, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
#[macro_use]
#[allow(missing_docs)]
mod macros;
mod derive;
pub mod builder;
pub mod error;
pub mod parser;
mod mkeymap;
mod output;
mod util;
const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
report at https://github.com/clap-rs/clap/issues";

574
vendor/clap_builder/src/macros.rs vendored Normal file
View File

@ -0,0 +1,574 @@
/// Allows you to pull the version from your Cargo.toml at compile time as
/// `MAJOR.MINOR.PATCH_PKGVERSION_PRE`
///
/// # Examples
///
/// ```no_run
/// # use clap_builder as clap;
/// # use clap::crate_version;
/// # use clap::Command;
/// let m = Command::new("cmd")
/// .version(crate_version!())
/// .get_matches();
/// ```
#[cfg(feature = "cargo")]
#[macro_export]
macro_rules! crate_version {
() => {
env!("CARGO_PKG_VERSION")
};
}
/// Allows you to pull the authors for the command from your Cargo.toml at
/// compile time in the form:
/// `"author1 lastname <author1@example.com>:author2 lastname <author2@example.com>"`
///
/// You can replace the colons with a custom separator by supplying a
/// replacement string, so, for example,
/// `crate_authors!(",\n")` would become
/// `"author1 lastname <author1@example.com>,\nauthor2 lastname <author2@example.com>,\nauthor3 lastname <author3@example.com>"`
///
/// # Examples
///
/// ```no_run
/// # use clap_builder as clap;
/// # use clap::crate_authors;
/// # use clap::Command;
/// let m = Command::new("cmd")
/// .author(crate_authors!("\n"))
/// .get_matches();
/// ```
#[cfg(feature = "cargo")]
#[macro_export]
macro_rules! crate_authors {
($sep:expr) => {{
static authors: &str = env!("CARGO_PKG_AUTHORS");
if authors.contains(':') {
static CACHED: std::sync::OnceLock<String> = std::sync::OnceLock::new();
let s = CACHED.get_or_init(|| authors.replace(':', $sep));
let s: &'static str = &*s;
s
} else {
authors
}
}};
() => {
env!("CARGO_PKG_AUTHORS")
};
}
/// Allows you to pull the description from your Cargo.toml at compile time.
///
/// # Examples
///
/// ```no_run
/// # use clap_builder as clap;
/// # use clap::crate_description;
/// # use clap::Command;
/// let m = Command::new("cmd")
/// .about(crate_description!())
/// .get_matches();
/// ```
#[cfg(feature = "cargo")]
#[macro_export]
macro_rules! crate_description {
() => {
env!("CARGO_PKG_DESCRIPTION")
};
}
/// Allows you to pull the name from your Cargo.toml at compile time.
///
/// **NOTE:** This macro extracts the name from an environment variable `CARGO_PKG_NAME`.
/// When the crate name is set to something different from the package name,
/// use environment variables `CARGO_CRATE_NAME` or `CARGO_BIN_NAME`.
/// See [the Cargo Book](https://doc.rust-lang.org/cargo/reference/environment-variables.html)
/// for more information.
///
/// # Examples
///
/// ```no_run
/// # use clap_builder as clap;
/// # use clap::crate_name;
/// # use clap::Command;
/// let m = Command::new(crate_name!())
/// .get_matches();
/// ```
#[cfg(feature = "cargo")]
#[macro_export]
macro_rules! crate_name {
() => {
env!("CARGO_PKG_NAME")
};
}
/// Allows you to build the `Command` instance from your Cargo.toml at compile time.
///
/// **NOTE:** Changing the values in your `Cargo.toml` does not trigger a re-build automatically,
/// and therefore won't change the generated output until you recompile.
///
/// In some cases you can "trick" the compiler into triggering a rebuild when your
/// `Cargo.toml` is changed by including this in your `src/main.rs` file
/// `include_str!("../Cargo.toml");`
///
/// # Examples
///
/// ```no_run
/// # use clap_builder as clap;
/// # use clap::command;
/// let m = command!().get_matches();
/// ```
#[cfg(feature = "cargo")]
#[macro_export]
macro_rules! command {
() => {{
$crate::command!($crate::crate_name!())
}};
($name:expr) => {{
let mut cmd = $crate::Command::new($name).version($crate::crate_version!());
let author = $crate::crate_authors!();
if !author.is_empty() {
cmd = cmd.author(author)
}
let about = $crate::crate_description!();
if !about.is_empty() {
cmd = cmd.about(about)
}
cmd
}};
}
/// Requires `cargo` feature flag to be enabled.
#[cfg(not(feature = "cargo"))]
#[macro_export]
macro_rules! command {
() => {{
compile_error!("`cargo` feature flag is required");
}};
($name:expr) => {{
compile_error!("`cargo` feature flag is required");
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! arg_impl {
( @string $val:ident ) => {
stringify!($val)
};
( @string $val:literal ) => {{
let ident_or_string_literal: &str = $val;
ident_or_string_literal
}};
( @string $val:tt ) => {
::std::compile_error!("Only identifiers or string literals supported");
};
( @string ) => {
None
};
( @char $val:ident ) => {{
let ident_or_char_literal = stringify!($val);
debug_assert_eq!(
ident_or_char_literal.len(),
1,
"Single-letter identifier expected, got {ident_or_char_literal}",
);
ident_or_char_literal.chars().next().unwrap()
}};
( @char $val:literal ) => {{
let ident_or_char_literal: char = $val;
ident_or_char_literal
}};
( @char ) => {{
None
}};
(
@arg
($arg:expr)
--$long:ident
$($tail:tt)*
) => {{
debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values");
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
let mut arg = $arg;
let long = $crate::arg_impl! { @string $long };
if arg.get_id() == "" {
arg = arg.id(long);
}
let action = $crate::ArgAction::SetTrue;
let arg = arg
.long(long)
.action(action);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
--$long:literal
$($tail:tt)*
) => {{
debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values");
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
let mut arg = $arg;
let long = $crate::arg_impl! { @string $long };
if arg.get_id() == "" {
arg = arg.id(long);
}
let action = $crate::ArgAction::SetTrue;
let arg = arg
.long(long)
.action(action);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
-$short:ident
$($tail:tt)*
) => {{
debug_assert_eq!($arg.get_long(), None, "Short flags should precede long flags");
debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values");
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
let action = $crate::ArgAction::SetTrue;
let arg = $arg
.short($crate::arg_impl! { @char $short })
.action(action);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
-$short:literal
$($tail:tt)*
) => {{
debug_assert_eq!($arg.get_long(), None, "Short flags should precede long flags");
debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values");
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
let action = $crate::ArgAction::SetTrue;
let arg = $arg
.short($crate::arg_impl! { @char $short })
.action(action);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
<$value_name:ident>
$($tail:tt)*
) => {{
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported");
let mut arg = $arg;
if arg.get_long().is_none() && arg.get_short().is_none() {
arg = arg.required(true);
}
let value_name = $crate::arg_impl! { @string $value_name };
if arg.get_id() == "" {
arg = arg.id(value_name);
}
let arg = arg
.value_name(value_name)
.action($crate::ArgAction::Set);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
<$value_name:literal>
$($tail:tt)*
) => {{
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported");
let mut arg = $arg;
if arg.get_long().is_none() && arg.get_short().is_none() {
arg = arg.required(true);
}
let value_name = $crate::arg_impl! { @string $value_name };
if arg.get_id() == "" {
arg = arg.id(value_name);
}
let arg = arg
.value_name(value_name)
.action($crate::ArgAction::Set);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
[$value_name:ident]
$($tail:tt)*
) => {{
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported");
let mut arg = $arg;
if arg.get_long().is_none() && arg.get_short().is_none() {
arg = arg.required(false);
} else {
arg = arg.num_args(0..=1);
}
let value_name = $crate::arg_impl! { @string $value_name };
if arg.get_id() == "" {
arg = arg.id(value_name);
}
let arg = arg
.value_name(value_name)
.action($crate::ArgAction::Set);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
[$value_name:literal]
$($tail:tt)*
) => {{
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported");
let mut arg = $arg;
if arg.get_long().is_none() && arg.get_short().is_none() {
arg = arg.required(false);
} else {
arg = arg.num_args(0..=1);
}
let value_name = $crate::arg_impl! { @string $value_name };
if arg.get_id() == "" {
arg = arg.id(value_name);
}
let arg = arg
.value_name(value_name)
.action($crate::ArgAction::Set);
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
...
$($tail:tt)*
) => {{
let arg = match $arg.get_action() {
$crate::ArgAction::Set => {
if $arg.get_long().is_none() && $arg.get_short().is_none() {
$arg.num_args(1..)
// Allow collecting arguments interleaved with flags
.action($crate::ArgAction::Append)
} else {
$arg.action($crate::ArgAction::Append)
}
},
$crate::ArgAction::SetTrue | $crate::ArgAction::Help | $crate::ArgAction::Version => {
$arg.action($crate::ArgAction::Count)
}
action => {
panic!("Unexpected action {action:?}")
}
};
let arg = $crate::arg_impl! {
@arg (arg) $($tail)*
};
arg
}};
(
@arg
($arg:expr)
$help:literal
) => {{
$arg.help($help)
}};
(
@arg
($arg:expr)
) => {{
$arg
}};
}
/// Create an [`Arg`] from a usage string.
///
/// Allows creation of basic settings for the [`Arg`].
///
/// **NOTE**: Not all settings may be set using the usage string method. Some properties are
/// only available via the builder pattern.
///
/// # Syntax
///
/// Usage strings typically following the form:
///
/// ```notrust
/// [explicit name] [short] [long] [value names] [...] [help string]
/// ```
///
/// ### Explicit Name
///
/// The name may be either a bare-word or a string, followed by a `:`, like `name:` or
/// `"name":`.
///
/// *Note:* This is an optional field, if it's omitted the argument will use one of the additional
/// fields as the name using the following priority order:
///
/// 1. Explicit Name
/// 2. Long
/// 3. Value Name
///
/// See [`Arg::id`][crate::Arg::id].
///
/// ### Short
///
/// A short flag is a `-` followed by either a bare-character or quoted character, like `-f` or
/// `-'f'`.
///
/// See [`Arg::short`][crate::Arg::short].
///
/// ### Long
///
/// A long flag is a `--` followed by either a bare-word or a string, like `--foo` or
/// `--"foo"`.
///
/// **NOTE:** Dashes in the long name (e.g. `--foo-bar`) is not supported and quoting is required
/// (e.g. `--"foo-bar"`).
///
/// See [`Arg::long`][crate::Arg::long].
///
/// ### Values (Value Notation)
///
/// This is set by placing bare-word between:
/// - `[]` like `[FOO]`
/// - Positional argument: optional
/// - Named argument: optional value
/// - `<>` like `<FOO>`: required
///
/// See [`Arg::value_name`][crate::Arg::value_name].
///
/// ### `...`
///
/// `...` (three consecutive dots/periods) specifies that this argument may occur multiple
/// times (not to be confused with multiple values per occurrence).
///
/// See [`ArgAction::Count`][crate::ArgAction::Count] and [`ArgAction::Append`][crate::ArgAction::Append].
///
/// ### Help String
///
/// The help string is denoted between a pair of double quotes `""` and may contain any
/// characters.
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, arg};
/// let cmd = Command::new("prog")
/// .args(&[
/// arg!(--config <FILE> "a required file for the configuration and no short"),
/// arg!(-d --debug ... "turns on debugging information and allows multiples"),
/// arg!([input] "an optional input file to use")
/// ]);
///
/// let m = cmd.try_get_matches_from(["prog", "--config", "file.toml"]).unwrap();
/// assert_eq!(m.get_one::<String>("config").unwrap(), "file.toml");
/// assert_eq!(*m.get_one::<u8>("debug").unwrap(), 0);
/// assert_eq!(m.get_one::<String>("input"), None);
/// ```
/// [`Arg`]: crate::Arg
#[macro_export]
macro_rules! arg {
( $name:ident: $($tail:tt)+ ) => {{
let arg = $crate::Arg::new($crate::arg_impl! { @string $name });
let arg = $crate::arg_impl! {
@arg (arg) $($tail)+
};
arg
}};
( $($tail:tt)+ ) => {{
let arg = $crate::Arg::default();
let arg = $crate::arg_impl! {
@arg (arg) $($tail)+
};
debug_assert_ne!(arg.get_id(), "", "Without a value or long flag, the `name:` prefix is required");
arg
}};
}
#[cfg(feature = "debug")]
macro_rules! debug {
($($arg:tt)*) => ({
use std::fmt::Write as _;
let hint = anstyle::Style::new().dimmed();
let module_path = module_path!();
let body = format!($($arg)*);
let mut styled = $crate::builder::StyledStr::new();
let _ = write!(styled, "{}[{module_path:>28}]{body}{}\n", hint.render(), hint.render_reset());
let color = $crate::output::fmt::Colorizer::new($crate::output::fmt::Stream::Stderr, $crate::ColorChoice::Auto).with_content(styled);
let _ = color.print();
})
}
#[cfg(not(feature = "debug"))]
macro_rules! debug {
($($arg:tt)*) => {};
}
macro_rules! ok {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(err) => {
return Err(err);
}
}
};
}
macro_rules! some {
($expr:expr) => {
match $expr {
Some(val) => val,
None => {
return None;
}
}
};
}

188
vendor/clap_builder/src/mkeymap.rs vendored Normal file
View File

@ -0,0 +1,188 @@
use std::iter::Iterator;
use std::ops::Index;
use crate::builder::OsStr;
use crate::Arg;
use crate::INTERNAL_ERROR_MSG;
#[derive(PartialEq, Eq, Debug, Clone)]
pub(crate) struct Key {
key: KeyType,
index: usize,
}
#[derive(Default, PartialEq, Eq, Debug, Clone)]
pub(crate) struct MKeyMap {
/// All of the arguments.
args: Vec<Arg>,
// Cache part:
/// Will be set after `_build()`.
keys: Vec<Key>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub(crate) enum KeyType {
Short(char),
Long(OsStr),
Position(usize),
}
impl KeyType {
pub(crate) fn is_position(&self) -> bool {
matches!(self, KeyType::Position(_))
}
}
impl PartialEq<usize> for KeyType {
fn eq(&self, rhs: &usize) -> bool {
match self {
KeyType::Position(x) => x == rhs,
_ => false,
}
}
}
impl PartialEq<&str> for KeyType {
fn eq(&self, rhs: &&str) -> bool {
match self {
KeyType::Long(l) => l == rhs,
_ => false,
}
}
}
impl PartialEq<str> for KeyType {
fn eq(&self, rhs: &str) -> bool {
match self {
KeyType::Long(l) => l == rhs,
_ => false,
}
}
}
impl PartialEq<OsStr> for KeyType {
fn eq(&self, rhs: &OsStr) -> bool {
match self {
KeyType::Long(l) => l == rhs,
_ => false,
}
}
}
impl PartialEq<char> for KeyType {
fn eq(&self, rhs: &char) -> bool {
match self {
KeyType::Short(c) => c == rhs,
_ => false,
}
}
}
impl MKeyMap {
/// If any arg has corresponding key in this map, we can search the key with
/// u64(for positional argument), char(for short flag), &str and OsString
/// (for long flag)
pub(crate) fn contains<K>(&self, key: K) -> bool
where
KeyType: PartialEq<K>,
{
self.keys.iter().any(|x| x.key == key)
}
/// Push an argument in the map.
pub(crate) fn push(&mut self, new_arg: Arg) {
self.args.push(new_arg);
}
/// Find the arg have corresponding key in this map, we can search the key
/// with u64(for positional argument), char(for short flag), &str and
/// OsString (for long flag)
pub(crate) fn get<K: ?Sized>(&self, key: &K) -> Option<&Arg>
where
KeyType: PartialEq<K>,
{
self.keys
.iter()
.find(|k| &k.key == key)
.map(|k| &self.args[k.index])
}
/// Return iterators of all keys.
pub(crate) fn keys(&self) -> impl Iterator<Item = &KeyType> {
self.keys.iter().map(|x| &x.key)
}
/// Return iterators of all args.
pub(crate) fn args(&self) -> impl Iterator<Item = &Arg> {
self.args.iter()
}
/// Return mutable iterators of all args.
pub(crate) fn args_mut(&mut self) -> impl Iterator<Item = &mut Arg> {
self.args.iter_mut()
}
/// Mutate every argument.
pub(crate) fn mut_args<F>(&mut self, f: F)
where
F: FnMut(Arg) -> Arg,
{
let mut args = std::mem::take(&mut self.args);
self.args.extend(args.drain(..).map(f));
}
/// We need a lazy build here since some we may change args after creating
/// the map, you can checkout who uses `args_mut`.
pub(crate) fn _build(&mut self) {
// There will be at least as many keys as args, so that is a good starting point
self.keys.reserve(self.args.len());
for (i, arg) in self.args.iter().enumerate() {
append_keys(&mut self.keys, arg, i);
}
}
/// Remove an arg in the graph by Id, usually used by `mut_arg`. Return
/// `Some(arg)` if removed.
pub(crate) fn remove_by_name(&mut self, name: &str) -> Option<Arg> {
self.args
.iter()
.position(|arg| arg.id == name)
// since it's a cold function, using this wouldn't hurt much
.map(|i| self.args.remove(i))
}
}
impl Index<&'_ KeyType> for MKeyMap {
type Output = Arg;
fn index(&self, key: &KeyType) -> &Self::Output {
self.get(key).expect(INTERNAL_ERROR_MSG)
}
}
/// Generate key types for an specific Arg.
fn append_keys(keys: &mut Vec<Key>, arg: &Arg, index: usize) {
if let Some(pos_index) = arg.index {
let key = KeyType::Position(pos_index);
keys.push(Key { key, index });
} else {
if let Some(short) = arg.short {
let key = KeyType::Short(short);
keys.push(Key { key, index });
}
if let Some(long) = arg.long.clone() {
let key = KeyType::Long(long.into());
keys.push(Key { key, index });
}
for (short, _) in arg.short_aliases.iter() {
let key = KeyType::Short(*short);
keys.push(Key { key, index });
}
for (long, _) in arg.aliases.iter() {
let key = KeyType::Long(long.into());
keys.push(Key { key, index });
}
}
}

83
vendor/clap_builder/src/output/fmt.rs vendored Normal file
View File

@ -0,0 +1,83 @@
use crate::builder::StyledStr;
use crate::util::color::ColorChoice;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum Stream {
Stdout,
Stderr,
}
#[derive(Clone, Debug)]
pub(crate) struct Colorizer {
stream: Stream,
#[allow(unused)]
color_when: ColorChoice,
content: StyledStr,
}
impl Colorizer {
pub(crate) fn new(stream: Stream, color_when: ColorChoice) -> Self {
Colorizer {
stream,
color_when,
content: Default::default(),
}
}
pub(crate) fn with_content(mut self, content: StyledStr) -> Self {
self.content = content;
self
}
}
/// Printing methods.
impl Colorizer {
#[cfg(feature = "color")]
pub(crate) fn print(&self) -> std::io::Result<()> {
let color_when = match self.color_when {
ColorChoice::Always => anstream::ColorChoice::Always,
ColorChoice::Auto => anstream::ColorChoice::Auto,
ColorChoice::Never => anstream::ColorChoice::Never,
};
let mut stdout;
let mut stderr;
let writer: &mut dyn std::io::Write = match self.stream {
Stream::Stderr => {
stderr = anstream::AutoStream::new(std::io::stderr().lock(), color_when);
&mut stderr
}
Stream::Stdout => {
stdout = anstream::AutoStream::new(std::io::stdout().lock(), color_when);
&mut stdout
}
};
self.content.write_to(writer)
}
#[cfg(not(feature = "color"))]
pub(crate) fn print(&self) -> std::io::Result<()> {
// [e]println can't be used here because it panics
// if something went wrong. We don't want that.
match self.stream {
Stream::Stdout => {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
self.content.write_to(&mut stdout)
}
Stream::Stderr => {
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
self.content.write_to(&mut stderr)
}
}
}
}
/// Color-unaware printing. Never uses coloring.
impl std::fmt::Display for Colorizer {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.content.fmt(f)
}
}

39
vendor/clap_builder/src/output/help.rs vendored Normal file
View File

@ -0,0 +1,39 @@
#![cfg_attr(not(feature = "help"), allow(unused_variables))]
// Internal
use crate::builder::Command;
use crate::builder::StyledStr;
use crate::output::Usage;
/// Writes the parser help to the wrapped stream.
pub(crate) fn write_help(writer: &mut StyledStr, cmd: &Command, usage: &Usage<'_>, use_long: bool) {
debug!("write_help");
if let Some(h) = cmd.get_override_help() {
writer.push_styled(h);
} else {
#[cfg(feature = "help")]
{
use super::AutoHelp;
use super::HelpTemplate;
if let Some(tmpl) = cmd.get_help_template() {
HelpTemplate::new(writer, cmd, usage, use_long)
.write_templated_help(tmpl.as_styled_str());
} else {
AutoHelp::new(writer, cmd, usage, use_long).write_help();
}
}
#[cfg(not(feature = "help"))]
{
debug!("write_help: no help, `Command::override_help` and `help` is missing");
}
}
// Remove any lines from unused sections
writer.trim_start_lines();
// Remove any whitespace caused by book keeping
writer.trim_end();
// Ensure there is still a trailing newline
writer.push_str("\n");
}

File diff suppressed because it is too large Load Diff

23
vendor/clap_builder/src/output/mod.rs vendored Normal file
View File

@ -0,0 +1,23 @@
mod help;
#[cfg(feature = "help")]
mod help_template;
mod usage;
pub(crate) mod fmt;
#[cfg(feature = "help")]
pub(crate) mod textwrap;
pub(crate) use self::help::write_help;
#[cfg(feature = "help")]
pub(crate) use self::help_template::AutoHelp;
#[cfg(feature = "help")]
pub(crate) use self::help_template::HelpTemplate;
#[cfg(feature = "help")]
pub(crate) use self::textwrap::core::display_width;
#[cfg(feature = "help")]
pub(crate) use self::textwrap::wrap;
pub(crate) use self::usage::Usage;
pub(crate) const TAB: &str = " ";
#[cfg(feature = "help")]
pub(crate) const TAB_WIDTH: usize = TAB.len();

View File

@ -0,0 +1,158 @@
/// Compute the display width of `text`
///
/// # Examples
///
/// **Note:** When the `unicode` Cargo feature is disabled, all characters are presumed to take up
/// 1 width. With the feature enabled, function will correctly deal with [combining characters] in
/// their decomposed form (see [Unicode equivalence]).
///
/// An example of a decomposed character is “é”, which can be decomposed into: “e” followed by a
/// combining acute accent: “◌́”. Without the `unicode` Cargo feature, every `char` has a width of
/// 1. This includes the combining accent:
///
/// ## Emojis and CJK Characters
///
/// Characters such as emojis and [CJK characters] used in the
/// Chinese, Japanese, and Korean languages are seen as double-width,
/// even if the `unicode-width` feature is disabled:
///
/// # Limitations
///
/// The displayed width of a string cannot always be computed from the
/// string alone. This is because the width depends on the rendering
/// engine used. This is particularly visible with [emoji modifier
/// sequences] where a base emoji is modified with, e.g., skin tone or
/// hair color modifiers. It is up to the rendering engine to detect
/// this and to produce a suitable emoji.
///
/// A simple example is “❤️”, which consists of “❤” (U+2764: Black
/// Heart Symbol) followed by U+FE0F (Variation Selector-16). By
/// itself, “❤” is a black heart, but if you follow it with the
/// variant selector, you may get a wider red heart.
///
/// A more complex example would be “👨‍🦰” which should depict a man
/// with red hair. Here the computed width is too large — and the
/// width differs depending on the use of the `unicode-width` feature:
///
/// This happens because the grapheme consists of three code points:
/// “👨” (U+1F468: Man), Zero Width Joiner (U+200D), and “🦰”
/// (U+1F9B0: Red Hair). You can see them above in the test. With
/// `unicode-width` enabled, the ZWJ is correctly seen as having zero
/// width, without it is counted as a double-width character.
///
/// ## Terminal Support
///
/// Modern browsers typically do a great job at combining characters
/// as shown above, but terminals often struggle more. As an example,
/// Gnome Terminal version 3.38.1, shows “❤️” as a big red heart, but
/// shows "👨‍🦰" as “👨🦰”.
///
/// [combining characters]: https://en.wikipedia.org/wiki/Combining_character
/// [Unicode equivalence]: https://en.wikipedia.org/wiki/Unicode_equivalence
/// [CJK characters]: https://en.wikipedia.org/wiki/CJK_characters
/// [emoji modifier sequences]: https://unicode.org/emoji/charts/full-emoji-modifiers.html
#[inline(never)]
pub(crate) fn display_width(text: &str) -> usize {
let mut width = 0;
let mut control_sequence = false;
let control_terminate: char = 'm';
for ch in text.chars() {
if ch.is_ascii_control() {
control_sequence = true;
} else if control_sequence && ch == control_terminate {
control_sequence = false;
continue;
}
if !control_sequence {
width += ch_width(ch);
}
}
width
}
#[cfg(feature = "unicode")]
fn ch_width(ch: char) -> usize {
unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0)
}
#[cfg(not(feature = "unicode"))]
fn ch_width(_: char) -> usize {
1
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "unicode")]
use unicode_width::UnicodeWidthChar;
#[test]
fn emojis_have_correct_width() {
use unic_emoji_char::is_emoji;
// Emojis in the Basic Latin (ASCII) and Latin-1 Supplement
// blocks all have a width of 1 column. This includes
// characters such as '#' and '©'.
for ch in '\u{1}'..'\u{FF}' {
if is_emoji(ch) {
let desc = format!("{:?} U+{:04X}", ch, ch as u32);
#[cfg(feature = "unicode")]
assert_eq!(ch.width().unwrap(), 1, "char: {desc}");
#[cfg(not(feature = "unicode"))]
assert_eq!(ch_width(ch), 1, "char: {desc}");
}
}
// Emojis in the remaining blocks of the Basic Multilingual
// Plane (BMP), in the Supplementary Multilingual Plane (SMP),
// and in the Supplementary Ideographic Plane (SIP), are all 1
// or 2 columns wide when unicode-width is used, and always 2
// columns wide otherwise. This includes all of our favorite
// emojis such as 😊.
for ch in '\u{FF}'..'\u{2FFFF}' {
if is_emoji(ch) {
let desc = format!("{:?} U+{:04X}", ch, ch as u32);
#[cfg(feature = "unicode")]
assert!(ch.width().unwrap() <= 2, "char: {desc}");
#[cfg(not(feature = "unicode"))]
assert_eq!(ch_width(ch), 1, "char: {desc}");
}
}
// The remaining planes contain almost no assigned code points
// and thus also no emojis.
}
#[test]
#[cfg(feature = "unicode")]
fn display_width_works() {
assert_eq!("Café Plain".len(), 11); // “é” is two bytes
assert_eq!(display_width("Café Plain"), 10);
}
#[test]
#[cfg(feature = "unicode")]
fn display_width_narrow_emojis() {
assert_eq!(display_width(""), 1);
}
#[test]
#[cfg(feature = "unicode")]
fn display_width_narrow_emojis_variant_selector() {
assert_eq!(display_width("\u{fe0f}"), 1);
}
#[test]
#[cfg(feature = "unicode")]
fn display_width_emojis() {
assert_eq!(display_width("😂😭🥺🤣✨😍🙏🥰😊🔥"), 20);
}
}

View File

@ -0,0 +1,122 @@
//! Fork of `textwrap` crate
//!
//! Benefits of forking:
//! - Pull in only what we need rather than relying on the compiler to remove what we don't need
//! - `LineWrapper` is able to incrementally wrap which will help with `StyledStr
pub(crate) mod core;
#[cfg(feature = "wrap_help")]
pub(crate) mod word_separators;
#[cfg(feature = "wrap_help")]
pub(crate) mod wrap_algorithms;
#[cfg(feature = "wrap_help")]
pub(crate) fn wrap(content: &str, hard_width: usize) -> String {
let mut wrapper = wrap_algorithms::LineWrapper::new(hard_width);
let mut total = Vec::new();
for line in content.split_inclusive('\n') {
wrapper.reset();
let line = word_separators::find_words_ascii_space(line).collect::<Vec<_>>();
total.extend(wrapper.wrap(line));
}
total.join("")
}
#[cfg(not(feature = "wrap_help"))]
pub(crate) fn wrap(content: &str, _hard_width: usize) -> String {
content.to_owned()
}
#[cfg(test)]
#[cfg(feature = "wrap_help")]
mod test {
/// Compatibility shim to keep textwrap's tests
fn wrap(content: &str, hard_width: usize) -> Vec<String> {
super::wrap(content, hard_width)
.trim_end()
.split('\n')
.map(|s| s.to_owned())
.collect::<Vec<_>>()
}
#[test]
fn no_wrap() {
assert_eq!(wrap("foo", 10), vec!["foo"]);
}
#[test]
fn wrap_simple() {
assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]);
}
#[test]
fn to_be_or_not() {
assert_eq!(
wrap("To be, or not to be, that is the question.", 10),
vec!["To be, or", "not to be,", "that is", "the", "question."]
);
}
#[test]
fn multiple_words_on_first_line() {
assert_eq!(wrap("foo bar baz", 10), vec!["foo bar", "baz"]);
}
#[test]
fn long_word() {
assert_eq!(wrap("foo", 0), vec!["foo"]);
}
#[test]
fn long_words() {
assert_eq!(wrap("foo bar", 0), vec!["foo", "bar"]);
}
#[test]
fn max_width() {
assert_eq!(wrap("foo bar", usize::MAX), vec!["foo bar"]);
let text = "Hello there! This is some English text. \
It should not be wrapped given the extents below.";
assert_eq!(wrap(text, usize::MAX), vec![text]);
}
#[test]
fn leading_whitespace() {
assert_eq!(wrap(" foo bar", 6), vec![" foo", " bar"]);
}
#[test]
fn leading_whitespace_empty_first_line() {
// If there is no space for the first word, the first line
// will be empty. This is because the string is split into
// words like [" ", "foobar ", "baz"], which puts "foobar " on
// the second line. We never output trailing whitespace
assert_eq!(wrap(" foobar baz", 6), vec!["", " foobar", " baz"]);
}
#[test]
fn trailing_whitespace() {
// Whitespace is only significant inside a line. After a line
// gets too long and is broken, the first word starts in
// column zero and is not indented.
assert_eq!(wrap("foo bar baz ", 5), vec!["foo", "bar", "baz"]);
}
#[test]
fn issue_99() {
// We did not reset the in_whitespace flag correctly and did
// not handle single-character words after a line break.
assert_eq!(
wrap("aaabbbccc x yyyzzzwww", 9),
vec!["aaabbbccc", "x", "yyyzzzwww"]
);
}
#[test]
fn issue_129() {
// The dash is an em-dash which takes up four bytes. We used
// to panic since we tried to index into the character.
assert_eq!(wrap("x x", 1), vec!["x", "", "x"]);
}
}

View File

@ -0,0 +1,92 @@
pub(crate) fn find_words_ascii_space(line: &str) -> impl Iterator<Item = &'_ str> + '_ {
let mut start = 0;
let mut in_whitespace = false;
let mut char_indices = line.char_indices();
std::iter::from_fn(move || {
for (idx, ch) in char_indices.by_ref() {
let next_whitespace = ch == ' ';
if in_whitespace && !next_whitespace {
let word = &line[start..idx];
start = idx;
in_whitespace = next_whitespace;
return Some(word);
}
in_whitespace = next_whitespace;
}
if start < line.len() {
let word = &line[start..];
start = line.len();
return Some(word);
}
None
})
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_find_words {
($ascii_name:ident,
$([ $line:expr, $ascii_words:expr ]),+) => {
#[test]
fn $ascii_name() {
$(
let expected_words: Vec<&str> = $ascii_words.to_vec();
let actual_words = find_words_ascii_space($line)
.collect::<Vec<_>>();
assert_eq!(actual_words, expected_words, "Line: {:?}", $line);
)+
}
};
}
test_find_words!(ascii_space_empty, ["", []]);
test_find_words!(ascii_single_word, ["foo", ["foo"]]);
test_find_words!(ascii_two_words, ["foo bar", ["foo ", "bar"]]);
test_find_words!(
ascii_multiple_words,
["foo bar", ["foo ", "bar"]],
["x y z", ["x ", "y ", "z"]]
);
test_find_words!(ascii_only_whitespace, [" ", [" "]], [" ", [" "]]);
test_find_words!(
ascii_inter_word_whitespace,
["foo bar", ["foo ", "bar"]]
);
test_find_words!(ascii_trailing_whitespace, ["foo ", ["foo "]]);
test_find_words!(ascii_leading_whitespace, [" foo", [" ", "foo"]]);
test_find_words!(
ascii_multi_column_char,
["\u{1f920}", ["\u{1f920}"]] // cowboy emoji 🤠
);
test_find_words!(
ascii_hyphens,
["foo-bar", ["foo-bar"]],
["foo- bar", ["foo- ", "bar"]],
["foo - bar", ["foo ", "- ", "bar"]],
["foo -bar", ["foo ", "-bar"]]
);
test_find_words!(ascii_newline, ["foo\nbar", ["foo\nbar"]]);
test_find_words!(ascii_tab, ["foo\tbar", ["foo\tbar"]]);
test_find_words!(
ascii_non_breaking_space,
["foo\u{00A0}bar", ["foo\u{00A0}bar"]]
);
}

View File

@ -0,0 +1,63 @@
use super::core::display_width;
#[derive(Debug)]
pub(crate) struct LineWrapper<'w> {
hard_width: usize,
line_width: usize,
carryover: Option<&'w str>,
}
impl<'w> LineWrapper<'w> {
pub(crate) fn new(hard_width: usize) -> Self {
Self {
hard_width,
line_width: 0,
carryover: None,
}
}
pub(crate) fn reset(&mut self) {
self.line_width = 0;
self.carryover = None;
}
pub(crate) fn wrap(&mut self, mut words: Vec<&'w str>) -> Vec<&'w str> {
if self.carryover.is_none() {
if let Some(word) = words.first() {
if word.trim().is_empty() {
self.carryover = Some(*word);
} else {
self.carryover = Some("");
}
}
}
let mut i = 0;
while i < words.len() {
let word = &words[i];
let trimmed = word.trim_end();
let word_width = display_width(trimmed);
let trimmed_delta = word.len() - trimmed.len();
if i != 0 && self.hard_width < self.line_width + word_width {
if 0 < i {
let last = i - 1;
let trimmed = words[last].trim_end();
words[last] = trimmed;
}
self.line_width = 0;
words.insert(i, "\n");
i += 1;
if let Some(carryover) = self.carryover {
words.insert(i, carryover);
self.line_width += carryover.len();
i += 1;
}
}
self.line_width += word_width + trimmed_delta;
i += 1;
}
words
}
}

529
vendor/clap_builder/src/output/usage.rs vendored Normal file
View File

@ -0,0 +1,529 @@
#![cfg_attr(not(feature = "usage"), allow(unused_imports))]
#![cfg_attr(not(feature = "usage"), allow(unused_variables))]
#![cfg_attr(not(feature = "usage"), allow(clippy::manual_map))]
#![cfg_attr(not(feature = "usage"), allow(dead_code))]
// Internal
use crate::builder::ArgAction;
use crate::builder::StyledStr;
use crate::builder::Styles;
use crate::builder::{ArgPredicate, Command};
use crate::parser::ArgMatcher;
use crate::util::ChildGraph;
use crate::util::FlatSet;
use crate::util::Id;
static DEFAULT_SUB_VALUE_NAME: &str = "COMMAND";
const USAGE_SEP: &str = "\n ";
pub(crate) struct Usage<'cmd> {
cmd: &'cmd Command,
styles: &'cmd Styles,
required: Option<&'cmd ChildGraph<Id>>,
}
impl<'cmd> Usage<'cmd> {
pub(crate) fn new(cmd: &'cmd Command) -> Self {
Usage {
cmd,
styles: cmd.get_styles(),
required: None,
}
}
pub(crate) fn required(mut self, required: &'cmd ChildGraph<Id>) -> Self {
self.required = Some(required);
self
}
// Creates a usage string for display. This happens just after all arguments were parsed, but before
// any subcommands have been parsed (so as to give subcommands their own usage recursively)
pub(crate) fn create_usage_with_title(&self, used: &[Id]) -> Option<StyledStr> {
debug!("Usage::create_usage_with_title");
use std::fmt::Write as _;
let mut styled = StyledStr::new();
let _ = write!(
styled,
"{}Usage:{} ",
self.styles.get_usage().render(),
self.styles.get_usage().render_reset()
);
if self.write_usage_no_title(&mut styled, used) {
styled.trim_end();
} else {
return None;
}
debug!("Usage::create_usage_with_title: usage={styled}");
Some(styled)
}
// Creates a usage string (*without title*) if one was not provided by the user manually.
pub(crate) fn create_usage_no_title(&self, used: &[Id]) -> Option<StyledStr> {
debug!("Usage::create_usage_no_title");
let mut styled = StyledStr::new();
if self.write_usage_no_title(&mut styled, used) {
styled.trim_end();
debug!("Usage::create_usage_no_title: usage={styled}");
Some(styled)
} else {
None
}
}
// Creates a usage string (*without title*) if one was not provided by the user manually.
fn write_usage_no_title(&self, styled: &mut StyledStr, used: &[Id]) -> bool {
debug!("Usage::create_usage_no_title");
if let Some(u) = self.cmd.get_override_usage() {
styled.push_styled(u);
true
} else {
#[cfg(feature = "usage")]
{
if used.is_empty() {
self.write_help_usage(styled);
} else {
self.write_smart_usage(styled, used);
}
true
}
#[cfg(not(feature = "usage"))]
{
false
}
}
}
}
#[cfg(feature = "usage")]
impl<'cmd> Usage<'cmd> {
// Creates a usage string for display in help messages (i.e. not for errors)
fn write_help_usage(&self, styled: &mut StyledStr) {
debug!("Usage::write_help_usage");
use std::fmt::Write;
if self.cmd.has_visible_subcommands() && self.cmd.is_flatten_help_set() {
if !self.cmd.is_subcommand_required_set()
|| self.cmd.is_args_conflicts_with_subcommands_set()
{
self.write_arg_usage(styled, &[], true);
styled.trim_end();
let _ = write!(styled, "{}", USAGE_SEP);
}
let mut cmd = self.cmd.clone();
cmd.build();
for (i, sub) in cmd
.get_subcommands()
.filter(|c| !c.is_hide_set())
.enumerate()
{
if i != 0 {
styled.trim_end();
let _ = write!(styled, "{}", USAGE_SEP);
}
Usage::new(sub).write_usage_no_title(styled, &[]);
}
} else {
self.write_arg_usage(styled, &[], true);
self.write_subcommand_usage(styled);
}
}
// Creates a context aware usage string, or "smart usage" from currently used
// args, and requirements
fn write_smart_usage(&self, styled: &mut StyledStr, used: &[Id]) {
debug!("Usage::create_smart_usage");
use std::fmt::Write;
let placeholder = &self.styles.get_placeholder();
self.write_arg_usage(styled, used, true);
if self.cmd.is_subcommand_required_set() {
let value_name = self
.cmd
.get_subcommand_value_name()
.unwrap_or(DEFAULT_SUB_VALUE_NAME);
let _ = write!(
styled,
"{}<{value_name}>{}",
placeholder.render(),
placeholder.render_reset()
);
}
}
fn write_arg_usage(&self, styled: &mut StyledStr, used: &[Id], incl_reqs: bool) {
debug!("Usage::write_arg_usage; incl_reqs={incl_reqs:?}");
use std::fmt::Write as _;
let literal = &self.styles.get_literal();
let placeholder = &self.styles.get_placeholder();
let bin_name = self.cmd.get_usage_name_fallback();
if !bin_name.is_empty() {
// the trim won't properly remove a leading space due to the formatting
let _ = write!(
styled,
"{}{bin_name}{} ",
literal.render(),
literal.render_reset()
);
}
if used.is_empty() && self.needs_options_tag() {
let _ = write!(
styled,
"{}[OPTIONS]{} ",
placeholder.render(),
placeholder.render_reset()
);
}
self.write_args(styled, used, !incl_reqs);
}
fn write_subcommand_usage(&self, styled: &mut StyledStr) {
debug!("Usage::write_subcommand_usage");
use std::fmt::Write as _;
// incl_reqs is only false when this function is called recursively
if self.cmd.has_visible_subcommands() || self.cmd.is_allow_external_subcommands_set() {
let literal = &self.styles.get_literal();
let placeholder = &self.styles.get_placeholder();
let value_name = self
.cmd
.get_subcommand_value_name()
.unwrap_or(DEFAULT_SUB_VALUE_NAME);
if self.cmd.is_subcommand_negates_reqs_set()
|| self.cmd.is_args_conflicts_with_subcommands_set()
{
styled.trim_end();
let _ = write!(styled, "{}", USAGE_SEP);
if self.cmd.is_args_conflicts_with_subcommands_set() {
let bin_name = self.cmd.get_usage_name_fallback();
// Short-circuit full usage creation since no args will be relevant
let _ = write!(
styled,
"{}{bin_name}{} ",
literal.render(),
literal.render_reset()
);
} else {
self.write_arg_usage(styled, &[], false);
}
let _ = write!(
styled,
"{}<{value_name}>{}",
placeholder.render(),
placeholder.render_reset()
);
} else if self.cmd.is_subcommand_required_set() {
let _ = write!(
styled,
"{}<{value_name}>{}",
placeholder.render(),
placeholder.render_reset()
);
} else {
let _ = write!(
styled,
"{}[{value_name}]{}",
placeholder.render(),
placeholder.render_reset()
);
}
}
}
// Determines if we need the `[OPTIONS]` tag in the usage string
fn needs_options_tag(&self) -> bool {
debug!("Usage::needs_options_tag");
'outer: for f in self.cmd.get_non_positionals() {
debug!("Usage::needs_options_tag:iter: f={}", f.get_id());
// Don't print `[OPTIONS]` just for help or version
if f.get_long() == Some("help") || f.get_long() == Some("version") {
debug!("Usage::needs_options_tag:iter Option is built-in");
continue;
}
match f.get_action() {
ArgAction::Set
| ArgAction::Append
| ArgAction::SetTrue
| ArgAction::SetFalse
| ArgAction::Count => {}
ArgAction::Help
| ArgAction::HelpShort
| ArgAction::HelpLong
| ArgAction::Version => {
debug!("Usage::needs_options_tag:iter Option is built-in");
continue;
}
}
if f.is_hide_set() {
debug!("Usage::needs_options_tag:iter Option is hidden");
continue;
}
if f.is_required_set() {
debug!("Usage::needs_options_tag:iter Option is required");
continue;
}
for grp_s in self.cmd.groups_for_arg(f.get_id()) {
debug!("Usage::needs_options_tag:iter:iter: grp_s={grp_s:?}");
if self.cmd.get_groups().any(|g| g.id == grp_s && g.required) {
debug!("Usage::needs_options_tag:iter:iter: Group is required");
continue 'outer;
}
}
debug!("Usage::needs_options_tag:iter: [OPTIONS] required");
return true;
}
debug!("Usage::needs_options_tag: [OPTIONS] not required");
false
}
// Returns the required args in usage string form by fully unrolling all groups
pub(crate) fn write_args(&self, styled: &mut StyledStr, incls: &[Id], force_optional: bool) {
debug!("Usage::write_args: incls={incls:?}",);
use std::fmt::Write as _;
let literal = &self.styles.get_literal();
let required_owned;
let required = if let Some(required) = self.required {
required
} else {
required_owned = self.cmd.required_graph();
&required_owned
};
let mut unrolled_reqs = Vec::new();
for a in required.iter() {
let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
let required = match val {
ArgPredicate::Equals(_) => false,
ArgPredicate::IsPresent => true,
};
required.then(|| req_arg.clone())
};
for aa in self.cmd.unroll_arg_requires(is_relevant, a) {
// if we don't check for duplicates here this causes duplicate error messages
// see https://github.com/clap-rs/clap/issues/2770
unrolled_reqs.push(aa);
}
// always include the required arg itself. it will not be enumerated
// by unroll_requirements_for_arg.
unrolled_reqs.push(a.clone());
}
debug!("Usage::get_args: unrolled_reqs={unrolled_reqs:?}");
let mut required_groups_members = FlatSet::new();
let mut required_groups = FlatSet::new();
for req in unrolled_reqs.iter().chain(incls.iter()) {
if self.cmd.find_group(req).is_some() {
let group_members = self.cmd.unroll_args_in_group(req);
let elem = self.cmd.format_group(req);
required_groups.insert(elem);
required_groups_members.extend(group_members);
} else {
debug_assert!(self.cmd.find(req).is_some());
}
}
let mut required_opts = FlatSet::new();
let mut required_positionals = Vec::new();
for req in unrolled_reqs.iter().chain(incls.iter()) {
if let Some(arg) = self.cmd.find(req) {
if required_groups_members.contains(arg.get_id()) {
continue;
}
let stylized = arg.stylized(self.styles, Some(!force_optional));
if let Some(index) = arg.get_index() {
let new_len = index + 1;
if required_positionals.len() < new_len {
required_positionals.resize(new_len, None);
}
required_positionals[index] = Some(stylized);
} else {
required_opts.insert(stylized);
}
} else {
debug_assert!(self.cmd.find_group(req).is_some());
}
}
for pos in self.cmd.get_positionals() {
if pos.is_hide_set() {
continue;
}
if required_groups_members.contains(pos.get_id()) {
continue;
}
let index = pos.get_index().unwrap();
let new_len = index + 1;
if required_positionals.len() < new_len {
required_positionals.resize(new_len, None);
}
if required_positionals[index].is_some() {
if pos.is_last_set() {
let styled = required_positionals[index].take().unwrap();
let mut new = StyledStr::new();
let _ = write!(new, "{}--{} ", literal.render(), literal.render_reset());
new.push_styled(&styled);
required_positionals[index] = Some(new);
}
} else {
let mut styled;
if pos.is_last_set() {
styled = StyledStr::new();
let _ = write!(styled, "{}[--{} ", literal.render(), literal.render_reset());
styled.push_styled(&pos.stylized(self.styles, Some(true)));
let _ = write!(styled, "{}]{}", literal.render(), literal.render_reset());
} else {
styled = pos.stylized(self.styles, Some(false));
}
required_positionals[index] = Some(styled);
}
if pos.is_last_set() && force_optional {
required_positionals[index] = None;
}
}
if !force_optional {
for arg in required_opts {
styled.push_styled(&arg);
styled.push_str(" ");
}
for arg in required_groups {
styled.push_styled(&arg);
styled.push_str(" ");
}
}
for arg in required_positionals.into_iter().flatten() {
styled.push_styled(&arg);
styled.push_str(" ");
}
}
pub(crate) fn get_required_usage_from(
&self,
incls: &[Id],
matcher: Option<&ArgMatcher>,
incl_last: bool,
) -> Vec<StyledStr> {
debug!(
"Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
incls,
matcher.is_some(),
incl_last
);
let required_owned;
let required = if let Some(required) = self.required {
required
} else {
required_owned = self.cmd.required_graph();
&required_owned
};
let mut unrolled_reqs = Vec::new();
for a in required.iter() {
let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
let required = match val {
ArgPredicate::Equals(_) => {
if let Some(matcher) = matcher {
matcher.check_explicit(a, val)
} else {
false
}
}
ArgPredicate::IsPresent => true,
};
required.then(|| req_arg.clone())
};
for aa in self.cmd.unroll_arg_requires(is_relevant, a) {
// if we don't check for duplicates here this causes duplicate error messages
// see https://github.com/clap-rs/clap/issues/2770
unrolled_reqs.push(aa);
}
// always include the required arg itself. it will not be enumerated
// by unroll_requirements_for_arg.
unrolled_reqs.push(a.clone());
}
debug!("Usage::get_required_usage_from: unrolled_reqs={unrolled_reqs:?}");
let mut required_groups_members = FlatSet::new();
let mut required_groups = FlatSet::new();
for req in unrolled_reqs.iter().chain(incls.iter()) {
if self.cmd.find_group(req).is_some() {
let group_members = self.cmd.unroll_args_in_group(req);
let is_present = matcher
.map(|m| {
group_members
.iter()
.any(|arg| m.check_explicit(arg, &ArgPredicate::IsPresent))
})
.unwrap_or(false);
debug!("Usage::get_required_usage_from:iter:{req:?} group is_present={is_present}");
if is_present {
continue;
}
let elem = self.cmd.format_group(req);
required_groups.insert(elem);
required_groups_members.extend(group_members);
} else {
debug_assert!(self.cmd.find(req).is_some(), "`{req}` must exist");
}
}
let mut required_opts = FlatSet::new();
let mut required_positionals = Vec::new();
for req in unrolled_reqs.iter().chain(incls.iter()) {
if let Some(arg) = self.cmd.find(req) {
if required_groups_members.contains(arg.get_id()) {
continue;
}
let is_present = matcher
.map(|m| m.check_explicit(req, &ArgPredicate::IsPresent))
.unwrap_or(false);
debug!("Usage::get_required_usage_from:iter:{req:?} arg is_present={is_present}");
if is_present {
continue;
}
let stylized = arg.stylized(self.styles, Some(true));
if let Some(index) = arg.get_index() {
if !arg.is_last_set() || incl_last {
let new_len = index + 1;
if required_positionals.len() < new_len {
required_positionals.resize(new_len, None);
}
required_positionals[index] = Some(stylized);
}
} else {
required_opts.insert(stylized);
}
} else {
debug_assert!(self.cmd.find_group(req).is_some());
}
}
let mut ret_val = Vec::new();
ret_val.extend(required_opts);
ret_val.extend(required_groups);
for pos in required_positionals.into_iter().flatten() {
ret_val.push(pos);
}
debug!("Usage::get_required_usage_from: ret_val={ret_val:?}");
ret_val
}
}

View File

@ -0,0 +1,238 @@
// Std
use std::ffi::OsString;
use std::mem;
use std::ops::Deref;
// Internal
use crate::builder::{Arg, ArgPredicate, Command};
use crate::parser::Identifier;
use crate::parser::PendingArg;
use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource};
use crate::util::AnyValue;
use crate::util::FlatMap;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;
#[derive(Debug, Default)]
pub(crate) struct ArgMatcher {
matches: ArgMatches,
pending: Option<PendingArg>,
}
impl ArgMatcher {
pub(crate) fn new(_cmd: &Command) -> Self {
ArgMatcher {
matches: ArgMatches {
#[cfg(debug_assertions)]
valid_args: {
let args = _cmd.get_arguments().map(|a| a.get_id().clone());
let groups = _cmd.get_groups().map(|g| g.get_id().clone());
args.chain(groups).collect()
},
#[cfg(debug_assertions)]
valid_subcommands: _cmd
.get_subcommands()
.map(|sc| sc.get_name_str().clone())
.collect(),
..Default::default()
},
pending: None,
}
}
pub(crate) fn into_inner(self) -> ArgMatches {
self.matches
}
pub(crate) fn propagate_globals(&mut self, global_arg_vec: &[Id]) {
debug!("ArgMatcher::get_global_values: global_arg_vec={global_arg_vec:?}");
let mut vals_map = FlatMap::new();
self.fill_in_global_values(global_arg_vec, &mut vals_map);
}
fn fill_in_global_values(
&mut self,
global_arg_vec: &[Id],
vals_map: &mut FlatMap<Id, MatchedArg>,
) {
for global_arg in global_arg_vec {
if let Some(ma) = self.get(global_arg) {
// We have to check if the parent's global arg wasn't used but still exists
// such as from a default value.
//
// For example, `myprog subcommand --global-arg=value` where `--global-arg` defines
// a default value of `other` myprog would have an existing MatchedArg for
// `--global-arg` where the value is `other`
let to_update = if let Some(parent_ma) = vals_map.get(global_arg) {
if parent_ma.source() > ma.source() {
parent_ma
} else {
ma
}
} else {
ma
}
.clone();
vals_map.insert(global_arg.clone(), to_update);
}
}
if let Some(ref mut sc) = self.matches.subcommand {
let mut am = ArgMatcher {
matches: mem::take(&mut sc.matches),
pending: None,
};
am.fill_in_global_values(global_arg_vec, vals_map);
mem::swap(&mut am.matches, &mut sc.matches);
}
for (name, matched_arg) in vals_map.iter_mut() {
self.matches.args.insert(name.clone(), matched_arg.clone());
}
}
pub(crate) fn get(&self, arg: &Id) -> Option<&MatchedArg> {
self.matches.args.get(arg)
}
pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> {
self.matches.args.get_mut(arg)
}
pub(crate) fn remove(&mut self, arg: &Id) -> bool {
self.matches.args.remove(arg).is_some()
}
pub(crate) fn contains(&self, arg: &Id) -> bool {
self.matches.args.contains_key(arg)
}
pub(crate) fn arg_ids(&self) -> std::slice::Iter<'_, Id> {
self.matches.args.keys()
}
pub(crate) fn args(&self) -> crate::util::flat_map::Iter<'_, Id, MatchedArg> {
self.matches.args.iter()
}
pub(crate) fn entry(&mut self, arg: Id) -> crate::util::Entry<Id, MatchedArg> {
self.matches.args.entry(arg)
}
pub(crate) fn subcommand(&mut self, sc: SubCommand) {
self.matches.subcommand = Some(Box::new(sc));
}
pub(crate) fn subcommand_name(&self) -> Option<&str> {
self.matches.subcommand_name()
}
pub(crate) fn check_explicit(&self, arg: &Id, predicate: &ArgPredicate) -> bool {
self.get(arg)
.map(|a| a.check_explicit(predicate))
.unwrap_or_default()
}
pub(crate) fn start_custom_arg(&mut self, arg: &Arg, source: ValueSource) {
let id = arg.get_id().clone();
debug!("ArgMatcher::start_custom_arg: id={id:?}, source={source:?}");
let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
ma.set_source(source);
ma.new_val_group();
}
pub(crate) fn start_custom_group(&mut self, id: Id, source: ValueSource) {
debug!("ArgMatcher::start_custom_arg: id={id:?}, source={source:?}");
let ma = self.entry(id).or_insert(MatchedArg::new_group());
debug_assert_eq!(ma.type_id(), None);
ma.set_source(source);
ma.new_val_group();
}
pub(crate) fn start_occurrence_of_external(&mut self, cmd: &crate::Command) {
let id = Id::from_static_ref(Id::EXTERNAL);
debug!("ArgMatcher::start_occurrence_of_external: id={id:?}");
let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd));
debug_assert_eq!(
ma.type_id(),
Some(
cmd.get_external_subcommand_value_parser()
.expect(INTERNAL_ERROR_MSG)
.type_id()
)
);
ma.set_source(ValueSource::CommandLine);
ma.new_val_group();
}
pub(crate) fn add_val_to(&mut self, arg: &Id, val: AnyValue, raw_val: OsString) {
let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
ma.append_val(val, raw_val);
}
pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) {
let ma = self.get_mut(arg).expect(INTERNAL_ERROR_MSG);
ma.push_index(idx);
}
pub(crate) fn needs_more_vals(&self, o: &Arg) -> bool {
let num_pending = self
.pending
.as_ref()
.and_then(|p| (p.id == *o.get_id()).then_some(p.raw_vals.len()))
.unwrap_or(0);
debug!(
"ArgMatcher::needs_more_vals: o={}, pending={}",
o.get_id(),
num_pending
);
let expected = o.get_num_args().expect(INTERNAL_ERROR_MSG);
debug!("ArgMatcher::needs_more_vals: expected={expected}, actual={num_pending}");
expected.accepts_more(num_pending)
}
pub(crate) fn pending_arg_id(&self) -> Option<&Id> {
self.pending.as_ref().map(|p| &p.id)
}
pub(crate) fn pending_values_mut(
&mut self,
id: &Id,
ident: Option<Identifier>,
trailing_values: bool,
) -> &mut Vec<OsString> {
let pending = self.pending.get_or_insert_with(|| PendingArg {
id: id.clone(),
ident,
raw_vals: Default::default(),
trailing_idx: None,
});
debug_assert_eq!(pending.id, *id, "{INTERNAL_ERROR_MSG}");
if ident.is_some() {
debug_assert_eq!(pending.ident, ident, "{INTERNAL_ERROR_MSG}");
}
if trailing_values {
pending.trailing_idx.get_or_insert(pending.raw_vals.len());
}
&mut pending.raw_vals
}
pub(crate) fn start_trailing(&mut self) {
if let Some(pending) = &mut self.pending {
// Allow asserting its started on subsequent calls
pending.trailing_idx.get_or_insert(pending.raw_vals.len());
}
}
pub(crate) fn take_pending(&mut self) -> Option<PendingArg> {
self.pending.take()
}
}
impl Deref for ArgMatcher {
type Target = ArgMatches;
fn deref(&self) -> &Self::Target {
&self.matches
}
}

63
vendor/clap_builder/src/parser/error.rs vendored Normal file
View File

@ -0,0 +1,63 @@
use crate::util::AnyValueId;
/// Violation of [`ArgMatches`][crate::ArgMatches] assumptions
#[derive(Clone, Debug)]
#[allow(missing_copy_implementations)] // We might add non-Copy types in the future
#[non_exhaustive]
pub enum MatchesError {
/// Failed to downcast `AnyValue` to the specified type
#[non_exhaustive]
Downcast {
/// Type for value stored in [`ArgMatches`][crate::ArgMatches]
actual: AnyValueId,
/// The target type to downcast to
expected: AnyValueId,
},
/// Argument not defined in [`Command`][crate::Command]
#[non_exhaustive]
UnknownArgument {
// Missing `id` but blocked on a public id type which will hopefully come with `unstable-v4`
},
}
impl MatchesError {
#[cfg_attr(debug_assertions, track_caller)]
pub(crate) fn unwrap<T>(id: &str, r: Result<T, MatchesError>) -> T {
let err = match r {
Ok(t) => {
return t;
}
Err(err) => err,
};
panic!("Mismatch between definition and access of `{id}`. {err}",)
}
}
impl std::error::Error for MatchesError {}
impl std::fmt::Display for MatchesError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Downcast { actual, expected } => {
writeln!(
f,
"Could not downcast to {expected:?}, need to downcast to {actual:?}"
)
}
Self::UnknownArgument {} => {
writeln!(f, "Unknown argument or group id. Make sure you are using the argument id and not the short or long flags")
}
}
}
}
#[test]
fn check_auto_traits() {
static_assertions::assert_impl_all!(
MatchesError: Send,
Sync,
std::panic::RefUnwindSafe,
std::panic::UnwindSafe,
Unpin
);
}

View File

@ -0,0 +1 @@
pub(crate) mod suggestions;

View File

@ -0,0 +1,167 @@
#[cfg(feature = "suggestions")]
use std::cmp::Ordering;
// Internal
use crate::builder::Command;
/// Find strings from an iterable of `possible_values` similar to a given value `v`
/// Returns a Vec of all possible values that exceed a similarity threshold
/// sorted by ascending similarity, most similar comes last
#[cfg(feature = "suggestions")]
pub(crate) fn did_you_mean<T, I>(v: &str, possible_values: I) -> Vec<String>
where
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
let mut candidates: Vec<(f64, String)> = possible_values
.into_iter()
// GH #4660: using `jaro` because `jaro_winkler` implementation in `strsim-rs` is wrong
// causing strings with common prefix >=10 to be considered perfectly similar
.map(|pv| (strsim::jaro(v, pv.as_ref()), pv.as_ref().to_owned()))
// Confidence of 0.7 so that bar -> baz is suggested
.filter(|(confidence, _)| *confidence > 0.7)
.collect();
candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
candidates.into_iter().map(|(_, pv)| pv).collect()
}
#[cfg(not(feature = "suggestions"))]
pub(crate) fn did_you_mean<T, I>(_: &str, _: I) -> Vec<String>
where
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
Vec::new()
}
/// Returns a suffix that can be empty, or is the standard 'did you mean' phrase
pub(crate) fn did_you_mean_flag<'a, 'help, I, T>(
arg: &str,
remaining_args: &[&std::ffi::OsStr],
longs: I,
subcommands: impl IntoIterator<Item = &'a mut Command>,
) -> Option<(String, Option<String>)>
where
'help: 'a,
T: AsRef<str>,
I: IntoIterator<Item = T>,
{
use crate::mkeymap::KeyType;
match did_you_mean(arg, longs).pop() {
Some(candidate) => Some((candidate, None)),
None => subcommands
.into_iter()
.filter_map(|subcommand| {
subcommand._build_self(false);
let longs = subcommand.get_keymap().keys().filter_map(|a| {
if let KeyType::Long(v) = a {
Some(v.to_string_lossy().into_owned())
} else {
None
}
});
let subcommand_name = subcommand.get_name();
let candidate = some!(did_you_mean(arg, longs).pop());
let score = some!(remaining_args.iter().position(|x| subcommand_name == *x));
Some((score, (candidate, Some(subcommand_name.to_string()))))
})
.min_by_key(|(x, _)| *x)
.map(|(_, suggestion)| suggestion),
}
}
#[cfg(all(test, feature = "suggestions"))]
mod test {
use super::*;
#[test]
fn missing_letter() {
let p_vals = ["test", "possible", "values"];
assert_eq!(did_you_mean("tst", p_vals.iter()), vec!["test"]);
}
#[test]
fn ambiguous() {
let p_vals = ["test", "temp", "possible", "values"];
assert_eq!(did_you_mean("te", p_vals.iter()), vec!["test", "temp"]);
}
#[test]
fn unrelated() {
let p_vals = ["test", "possible", "values"];
assert_eq!(
did_you_mean("hahaahahah", p_vals.iter()),
Vec::<String>::new()
);
}
#[test]
fn best_fit() {
let p_vals = [
"test",
"possible",
"values",
"alignmentStart",
"alignmentScore",
];
assert_eq!(
did_you_mean("alignmentScorr", p_vals.iter()),
vec!["alignmentStart", "alignmentScore"]
);
}
#[test]
fn best_fit_long_common_prefix_issue_4660() {
let p_vals = ["alignmentScore", "alignmentStart"];
assert_eq!(
did_you_mean("alignmentScorr", p_vals.iter()),
vec!["alignmentStart", "alignmentScore"]
);
}
#[test]
fn flag_missing_letter() {
let p_vals = ["test", "possible", "values"];
assert_eq!(
did_you_mean_flag("tst", &[], p_vals.iter(), []),
Some(("test".to_owned(), None))
);
}
#[test]
fn flag_ambiguous() {
let p_vals = ["test", "temp", "possible", "values"];
assert_eq!(
did_you_mean_flag("te", &[], p_vals.iter(), []),
Some(("temp".to_owned(), None))
);
}
#[test]
fn flag_unrelated() {
let p_vals = ["test", "possible", "values"];
assert_eq!(
did_you_mean_flag("hahaahahah", &[], p_vals.iter(), []),
None
);
}
#[test]
fn flag_best_fit() {
let p_vals = [
"test",
"possible",
"values",
"alignmentStart",
"alignmentScore",
];
assert_eq!(
did_you_mean_flag("alignmentScorr", &[], p_vals.iter(), []),
Some(("alignmentScore".to_owned(), None))
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,225 @@
// Std
use std::{
ffi::{OsStr, OsString},
iter::{Cloned, Flatten},
slice::Iter,
};
use crate::builder::ArgPredicate;
use crate::parser::ValueSource;
use crate::util::eq_ignore_case;
use crate::util::AnyValue;
use crate::util::AnyValueId;
use crate::INTERNAL_ERROR_MSG;
#[derive(Debug, Clone)]
pub(crate) struct MatchedArg {
source: Option<ValueSource>,
indices: Vec<usize>,
type_id: Option<AnyValueId>,
vals: Vec<Vec<AnyValue>>,
raw_vals: Vec<Vec<OsString>>,
ignore_case: bool,
}
impl MatchedArg {
pub(crate) fn new_arg(arg: &crate::Arg) -> Self {
let ignore_case = arg.is_ignore_case_set();
Self {
source: None,
indices: Vec::new(),
type_id: Some(arg.get_value_parser().type_id()),
vals: Vec::new(),
raw_vals: Vec::new(),
ignore_case,
}
}
pub(crate) fn new_group() -> Self {
let ignore_case = false;
Self {
source: None,
indices: Vec::new(),
type_id: None,
vals: Vec::new(),
raw_vals: Vec::new(),
ignore_case,
}
}
pub(crate) fn new_external(cmd: &crate::Command) -> Self {
let ignore_case = false;
Self {
source: None,
indices: Vec::new(),
type_id: Some(
cmd.get_external_subcommand_value_parser()
.expect(INTERNAL_ERROR_MSG)
.type_id(),
),
vals: Vec::new(),
raw_vals: Vec::new(),
ignore_case,
}
}
pub(crate) fn indices(&self) -> Cloned<Iter<'_, usize>> {
self.indices.iter().cloned()
}
pub(crate) fn get_index(&self, index: usize) -> Option<usize> {
self.indices.get(index).cloned()
}
pub(crate) fn push_index(&mut self, index: usize) {
self.indices.push(index)
}
pub(crate) fn vals(&self) -> Iter<Vec<AnyValue>> {
self.vals.iter()
}
pub(crate) fn into_vals(self) -> Vec<Vec<AnyValue>> {
self.vals
}
pub(crate) fn vals_flatten(&self) -> Flatten<Iter<Vec<AnyValue>>> {
self.vals.iter().flatten()
}
pub(crate) fn into_vals_flatten(self) -> Flatten<std::vec::IntoIter<Vec<AnyValue>>> {
self.vals.into_iter().flatten()
}
pub(crate) fn raw_vals(&self) -> Iter<Vec<OsString>> {
self.raw_vals.iter()
}
pub(crate) fn raw_vals_flatten(&self) -> Flatten<Iter<Vec<OsString>>> {
self.raw_vals.iter().flatten()
}
pub(crate) fn first(&self) -> Option<&AnyValue> {
self.vals_flatten().next()
}
#[cfg(test)]
pub(crate) fn first_raw(&self) -> Option<&OsString> {
self.raw_vals_flatten().next()
}
pub(crate) fn new_val_group(&mut self) {
self.vals.push(vec![]);
self.raw_vals.push(vec![]);
}
pub(crate) fn append_val(&mut self, val: AnyValue, raw_val: OsString) {
// We assume there is always a group created before.
self.vals.last_mut().expect(INTERNAL_ERROR_MSG).push(val);
self.raw_vals
.last_mut()
.expect(INTERNAL_ERROR_MSG)
.push(raw_val);
}
pub(crate) fn num_vals(&self) -> usize {
self.vals.iter().map(|v| v.len()).sum()
}
// Will be used later
#[allow(dead_code)]
pub(crate) fn num_vals_last_group(&self) -> usize {
self.vals.last().map(|x| x.len()).unwrap_or(0)
}
pub(crate) fn all_val_groups_empty(&self) -> bool {
self.vals.iter().flatten().count() == 0
}
pub(crate) fn check_explicit(&self, predicate: &ArgPredicate) -> bool {
if self.source.map(|s| !s.is_explicit()).unwrap_or(false) {
return false;
}
match predicate {
ArgPredicate::Equals(val) => self.raw_vals_flatten().any(|v| {
if self.ignore_case {
// If `v` isn't utf8, it can't match `val`, so `OsStr::to_str` should be fine
eq_ignore_case(&v.to_string_lossy(), &val.to_string_lossy())
} else {
OsString::as_os_str(v) == OsStr::new(val)
}
}),
ArgPredicate::IsPresent => true,
}
}
pub(crate) fn source(&self) -> Option<ValueSource> {
self.source
}
pub(crate) fn set_source(&mut self, source: ValueSource) {
if let Some(existing) = self.source {
self.source = Some(existing.max(source));
} else {
self.source = Some(source)
}
}
pub(crate) fn type_id(&self) -> Option<AnyValueId> {
self.type_id
}
pub(crate) fn infer_type_id(&self, expected: AnyValueId) -> AnyValueId {
self.type_id()
.or_else(|| {
self.vals_flatten()
.map(|v| v.type_id())
.find(|actual| *actual != expected)
})
.unwrap_or(expected)
}
}
impl PartialEq for MatchedArg {
fn eq(&self, other: &MatchedArg) -> bool {
let MatchedArg {
source: self_source,
indices: self_indices,
type_id: self_type_id,
vals: _,
raw_vals: self_raw_vals,
ignore_case: self_ignore_case,
} = self;
let MatchedArg {
source: other_source,
indices: other_indices,
type_id: other_type_id,
vals: _,
raw_vals: other_raw_vals,
ignore_case: other_ignore_case,
} = other;
self_source == other_source
&& self_indices == other_indices
&& self_type_id == other_type_id
&& self_raw_vals == other_raw_vals
&& self_ignore_case == other_ignore_case
}
}
impl Eq for MatchedArg {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grouped_vals_first() {
let mut m = MatchedArg::new_group();
m.new_val_group();
m.new_val_group();
m.append_val(AnyValue::new(String::from("bbb")), "bbb".into());
m.append_val(AnyValue::new(String::from("ccc")), "ccc".into());
assert_eq!(m.first_raw(), Some(&OsString::from("bbb")));
}
}

View File

@ -0,0 +1,13 @@
mod arg_matches;
mod matched_arg;
mod value_source;
pub use arg_matches::IdsRef;
pub use arg_matches::RawValues;
pub use arg_matches::Values;
pub use arg_matches::ValuesRef;
pub use arg_matches::{ArgMatches, Indices};
pub use value_source::ValueSource;
pub(crate) use arg_matches::SubCommand;
pub(crate) use matched_arg::MatchedArg;

View File

@ -0,0 +1,17 @@
/// Origin of the argument's value
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum ValueSource {
/// Value came [`Arg::default_value`][crate::Arg::default_value]
DefaultValue,
/// Value came [`Arg::env`][crate::Arg::env]
EnvVariable,
/// Value was passed in on the command-line
CommandLine,
}
impl ValueSource {
pub(crate) fn is_explicit(self) -> bool {
self != Self::DefaultValue
}
}

25
vendor/clap_builder/src/parser/mod.rs vendored Normal file
View File

@ -0,0 +1,25 @@
//! [`Command`][crate::Command] line argument parser
mod arg_matcher;
mod error;
mod matches;
#[allow(clippy::module_inception)]
mod parser;
mod validator;
pub(crate) mod features;
pub(crate) use self::arg_matcher::ArgMatcher;
pub(crate) use self::matches::{MatchedArg, SubCommand};
pub(crate) use self::parser::Identifier;
pub(crate) use self::parser::PendingArg;
pub(crate) use self::parser::{ParseState, Parser};
pub(crate) use self::validator::get_possible_values_cli;
pub(crate) use self::validator::Validator;
pub use self::matches::IdsRef;
pub use self::matches::RawValues;
pub use self::matches::Values;
pub use self::matches::ValuesRef;
pub use self::matches::{ArgMatches, Indices, ValueSource};
pub use error::MatchesError;

1622
vendor/clap_builder/src/parser/parser.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,561 @@
// Internal
use crate::builder::StyledStr;
use crate::builder::{Arg, ArgGroup, ArgPredicate, Command, PossibleValue};
use crate::error::{Error, Result as ClapResult};
use crate::output::Usage;
use crate::parser::{ArgMatcher, ParseState};
use crate::util::ChildGraph;
use crate::util::FlatMap;
use crate::util::FlatSet;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;
pub(crate) struct Validator<'cmd> {
cmd: &'cmd Command,
required: ChildGraph<Id>,
}
impl<'cmd> Validator<'cmd> {
pub(crate) fn new(cmd: &'cmd Command) -> Self {
let required = cmd.required_graph();
Validator { cmd, required }
}
pub(crate) fn validate(
&mut self,
parse_state: ParseState,
matcher: &mut ArgMatcher,
) -> ClapResult<()> {
debug!("Validator::validate");
let conflicts = Conflicts::with_args(self.cmd, matcher);
let has_subcmd = matcher.subcommand_name().is_some();
if let ParseState::Opt(a) = parse_state {
debug!("Validator::validate: needs_val_of={a:?}");
let o = &self.cmd[&a];
let should_err = if let Some(v) = matcher.args.get(o.get_id()) {
v.all_val_groups_empty() && o.get_min_vals() != 0
} else {
true
};
if should_err {
return Err(Error::empty_value(
self.cmd,
&get_possible_values_cli(o)
.iter()
.filter(|pv| !pv.is_hide_set())
.map(|n| n.get_name().to_owned())
.collect::<Vec<_>>(),
o.to_string(),
));
}
}
if !has_subcmd && self.cmd.is_arg_required_else_help_set() {
let num_user_values = matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
.count();
if num_user_values == 0 {
let message = self.cmd.write_help_err(false);
return Err(Error::display_help_error(self.cmd, message));
}
}
if !has_subcmd && self.cmd.is_subcommand_required_set() {
let bn = self.cmd.get_bin_name_fallback();
return Err(Error::missing_subcommand(
self.cmd,
bn.to_string(),
self.cmd
.all_subcommand_names()
.map(|s| s.to_owned())
.collect::<Vec<_>>(),
Usage::new(self.cmd)
.required(&self.required)
.create_usage_with_title(&[]),
));
}
ok!(self.validate_conflicts(matcher, &conflicts));
if !(self.cmd.is_subcommand_negates_reqs_set() && has_subcmd) {
ok!(self.validate_required(matcher, &conflicts));
}
Ok(())
}
fn validate_conflicts(
&mut self,
matcher: &ArgMatcher,
conflicts: &Conflicts,
) -> ClapResult<()> {
debug!("Validator::validate_conflicts");
ok!(self.validate_exclusive(matcher));
for (arg_id, _) in matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
.filter(|(arg_id, _)| self.cmd.find(arg_id).is_some())
{
debug!("Validator::validate_conflicts::iter: id={arg_id:?}");
let conflicts = conflicts.gather_conflicts(self.cmd, arg_id);
ok!(self.build_conflict_err(arg_id, &conflicts, matcher));
}
Ok(())
}
fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> {
debug!("Validator::validate_exclusive");
let args_count = matcher
.args()
.filter(|(arg_id, matched)| {
matched.check_explicit(&crate::builder::ArgPredicate::IsPresent)
// Avoid including our own groups by checking none of them. If a group is present, the
// args for the group will be.
&& self.cmd.find(arg_id).is_some()
})
.count();
if args_count <= 1 {
// Nothing present to conflict with
return Ok(());
}
matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&crate::builder::ArgPredicate::IsPresent))
.filter_map(|(id, _)| {
debug!("Validator::validate_exclusive:iter:{id:?}");
self.cmd
.find(id)
// Find `arg`s which are exclusive but also appear with other args.
.filter(|&arg| arg.is_exclusive_set() && args_count > 1)
})
.next()
.map(|arg| {
// Throw an error for the first conflict found.
Err(Error::argument_conflict(
self.cmd,
arg.to_string(),
Vec::new(),
Usage::new(self.cmd)
.required(&self.required)
.create_usage_with_title(&[]),
))
})
.unwrap_or(Ok(()))
}
fn build_conflict_err(
&self,
name: &Id,
conflict_ids: &[Id],
matcher: &ArgMatcher,
) -> ClapResult<()> {
if conflict_ids.is_empty() {
return Ok(());
}
debug!("Validator::build_conflict_err: name={name:?}");
let mut seen = FlatSet::new();
let conflicts = conflict_ids
.iter()
.flat_map(|c_id| {
if self.cmd.find_group(c_id).is_some() {
self.cmd.unroll_args_in_group(c_id)
} else {
vec![c_id.clone()]
}
})
.filter_map(|c_id| {
seen.insert(c_id.clone()).then(|| {
let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG);
c_arg.to_string()
})
})
.collect();
let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG);
let usg = self.build_conflict_err_usage(matcher, conflict_ids);
Err(Error::argument_conflict(
self.cmd,
former_arg.to_string(),
conflicts,
usg,
))
}
fn build_conflict_err_usage(
&self,
matcher: &ArgMatcher,
conflicting_keys: &[Id],
) -> Option<StyledStr> {
let used_filtered: Vec<Id> = matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
.map(|(n, _)| n)
.filter(|n| {
// Filter out the args we don't want to specify.
self.cmd
.find(n)
.map(|a| !a.is_hide_set())
.unwrap_or_default()
})
.filter(|key| !conflicting_keys.contains(key))
.cloned()
.collect();
let required: Vec<Id> = used_filtered
.iter()
.filter_map(|key| self.cmd.find(key))
.flat_map(|arg| arg.requires.iter().map(|item| &item.1))
.filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key))
.chain(used_filtered.iter())
.cloned()
.collect();
Usage::new(self.cmd)
.required(&self.required)
.create_usage_with_title(&required)
}
fn gather_requires(&mut self, matcher: &ArgMatcher) {
debug!("Validator::gather_requires");
for (name, matched) in matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
{
debug!("Validator::gather_requires:iter:{name:?}");
if let Some(arg) = self.cmd.find(name) {
let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
let required = matched.check_explicit(val);
required.then(|| req_arg.clone())
};
for req in self.cmd.unroll_arg_requires(is_relevant, arg.get_id()) {
self.required.insert(req);
}
} else if let Some(g) = self.cmd.find_group(name) {
debug!("Validator::gather_requires:iter:{name:?}:group");
for r in &g.requires {
self.required.insert(r.clone());
}
}
}
}
fn validate_required(&mut self, matcher: &ArgMatcher, conflicts: &Conflicts) -> ClapResult<()> {
debug!("Validator::validate_required: required={:?}", self.required);
self.gather_requires(matcher);
let mut missing_required = Vec::new();
let mut highest_index = 0;
let is_exclusive_present = matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
.any(|(id, _)| {
self.cmd
.find(id)
.map(|arg| arg.is_exclusive_set())
.unwrap_or_default()
});
debug!("Validator::validate_required: is_exclusive_present={is_exclusive_present}");
for arg_or_group in self
.required
.iter()
.filter(|r| !matcher.check_explicit(r, &ArgPredicate::IsPresent))
{
debug!("Validator::validate_required:iter:aog={arg_or_group:?}");
if let Some(arg) = self.cmd.find(arg_or_group) {
debug!("Validator::validate_required:iter: This is an arg");
if !is_exclusive_present && !self.is_missing_required_ok(arg, conflicts) {
debug!(
"Validator::validate_required:iter: Missing {:?}",
arg.get_id()
);
missing_required.push(arg.get_id().clone());
if !arg.is_last_set() {
highest_index = highest_index.max(arg.get_index().unwrap_or(0));
}
}
} else if let Some(group) = self.cmd.find_group(arg_or_group) {
debug!("Validator::validate_required:iter: This is a group");
if !self
.cmd
.unroll_args_in_group(&group.id)
.iter()
.any(|a| matcher.check_explicit(a, &ArgPredicate::IsPresent))
{
debug!(
"Validator::validate_required:iter: Missing {:?}",
group.get_id()
);
missing_required.push(group.get_id().clone());
}
}
}
// Validate the conditionally required args
for a in self
.cmd
.get_arguments()
.filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
{
let mut required = false;
for (other, val) in &a.r_ifs {
if matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) {
debug!(
"Validator::validate_required:iter: Missing {:?}",
a.get_id()
);
required = true;
}
}
let match_all = a.r_ifs_all.iter().all(|(other, val)| {
matcher.check_explicit(other, &ArgPredicate::Equals(val.into()))
});
if match_all && !a.r_ifs_all.is_empty() {
debug!(
"Validator::validate_required:iter: Missing {:?}",
a.get_id()
);
required = true;
}
if (!a.r_unless.is_empty() || !a.r_unless_all.is_empty())
&& self.fails_arg_required_unless(a, matcher)
{
debug!(
"Validator::validate_required:iter: Missing {:?}",
a.get_id()
);
required = true;
}
if required {
missing_required.push(a.get_id().clone());
if !a.is_last_set() {
highest_index = highest_index.max(a.get_index().unwrap_or(0));
}
}
}
// For display purposes, include all of the preceding positional arguments
if !self.cmd.is_allow_missing_positional_set() {
for pos in self
.cmd
.get_positionals()
.filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
{
if pos.get_index() < Some(highest_index) {
debug!(
"Validator::validate_required:iter: Missing {:?}",
pos.get_id()
);
missing_required.push(pos.get_id().clone());
}
}
}
if !missing_required.is_empty() {
ok!(self.missing_required_error(matcher, missing_required));
}
Ok(())
}
fn is_missing_required_ok(&self, a: &Arg, conflicts: &Conflicts) -> bool {
debug!("Validator::is_missing_required_ok: {}", a.get_id());
if !conflicts.gather_conflicts(self.cmd, a.get_id()).is_empty() {
debug!("Validator::is_missing_required_ok: true (self)");
return true;
}
for group_id in self.cmd.groups_for_arg(a.get_id()) {
if !conflicts.gather_conflicts(self.cmd, &group_id).is_empty() {
debug!("Validator::is_missing_required_ok: true ({group_id})");
return true;
}
}
false
}
// Failing a required unless means, the arg's "unless" wasn't present, and neither were they
fn fails_arg_required_unless(&self, a: &Arg, matcher: &ArgMatcher) -> bool {
debug!("Validator::fails_arg_required_unless: a={:?}", a.get_id());
let exists = |id| matcher.check_explicit(id, &ArgPredicate::IsPresent);
(a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists))
&& !a.r_unless.iter().any(exists)
}
// `req_args`: an arg to include in the error even if not used
fn missing_required_error(
&self,
matcher: &ArgMatcher,
raw_req_args: Vec<Id>,
) -> ClapResult<()> {
debug!("Validator::missing_required_error; incl={raw_req_args:?}");
debug!(
"Validator::missing_required_error: reqs={:?}",
self.required
);
let usg = Usage::new(self.cmd).required(&self.required);
let req_args = {
#[cfg(feature = "usage")]
{
usg.get_required_usage_from(&raw_req_args, Some(matcher), true)
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
}
#[cfg(not(feature = "usage"))]
{
raw_req_args
.iter()
.map(|id| {
if let Some(arg) = self.cmd.find(id) {
arg.to_string()
} else if let Some(_group) = self.cmd.find_group(id) {
self.cmd.format_group(id).to_string()
} else {
debug_assert!(false, "id={id:?} is unknown");
"".to_owned()
}
})
.collect::<Vec<_>>()
}
};
debug!("Validator::missing_required_error: req_args={req_args:#?}");
let used: Vec<Id> = matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
.map(|(n, _)| n)
.filter(|n| {
// Filter out the args we don't want to specify.
self.cmd
.find(n)
.map(|a| !a.is_hide_set())
.unwrap_or_default()
})
.cloned()
.chain(raw_req_args)
.collect();
Err(Error::missing_required_argument(
self.cmd,
req_args,
usg.create_usage_with_title(&used),
))
}
}
#[derive(Default, Clone, Debug)]
struct Conflicts {
potential: FlatMap<Id, Vec<Id>>,
}
impl Conflicts {
fn with_args(cmd: &Command, matcher: &ArgMatcher) -> Self {
let mut potential = FlatMap::new();
potential.extend_unchecked(
matcher
.args()
.filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
.map(|(id, _)| {
let conf = gather_direct_conflicts(cmd, id);
(id.clone(), conf)
}),
);
Self { potential }
}
fn gather_conflicts(&self, cmd: &Command, arg_id: &Id) -> Vec<Id> {
debug!("Conflicts::gather_conflicts: arg={arg_id:?}");
let mut conflicts = Vec::new();
let arg_id_conflicts_storage;
let arg_id_conflicts = if let Some(arg_id_conflicts) = self.get_direct_conflicts(arg_id) {
arg_id_conflicts
} else {
// `is_missing_required_ok` is a case where we check not-present args for conflicts
arg_id_conflicts_storage = gather_direct_conflicts(cmd, arg_id);
&arg_id_conflicts_storage
};
for (other_arg_id, other_arg_id_conflicts) in self.potential.iter() {
if arg_id == other_arg_id {
continue;
}
if arg_id_conflicts.contains(other_arg_id) {
conflicts.push(other_arg_id.clone());
}
if other_arg_id_conflicts.contains(arg_id) {
conflicts.push(other_arg_id.clone());
}
}
debug!("Conflicts::gather_conflicts: conflicts={conflicts:?}");
conflicts
}
fn get_direct_conflicts(&self, arg_id: &Id) -> Option<&[Id]> {
self.potential.get(arg_id).map(Vec::as_slice)
}
}
fn gather_direct_conflicts(cmd: &Command, id: &Id) -> Vec<Id> {
let conf = if let Some(arg) = cmd.find(id) {
gather_arg_direct_conflicts(cmd, arg)
} else if let Some(group) = cmd.find_group(id) {
gather_group_direct_conflicts(group)
} else {
debug_assert!(false, "id={id:?} is unknown");
Vec::new()
};
debug!("Conflicts::gather_direct_conflicts id={id:?}, conflicts={conf:?}",);
conf
}
fn gather_arg_direct_conflicts(cmd: &Command, arg: &Arg) -> Vec<Id> {
let mut conf = arg.blacklist.clone();
for group_id in cmd.groups_for_arg(arg.get_id()) {
let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG);
conf.extend(group.conflicts.iter().cloned());
if !group.multiple {
for member_id in &group.args {
if member_id != arg.get_id() {
conf.push(member_id.clone());
}
}
}
}
// Overrides are implicitly conflicts
conf.extend(arg.overrides.iter().cloned());
conf
}
fn gather_group_direct_conflicts(group: &ArgGroup) -> Vec<Id> {
group.conflicts.clone()
}
pub(crate) fn get_possible_values_cli(a: &Arg) -> Vec<PossibleValue> {
if !a.is_takes_value_set() {
vec![]
} else {
a.get_value_parser()
.possible_values()
.map(|pvs| pvs.collect())
.unwrap_or_default()
}
}

View File

@ -0,0 +1,127 @@
#[derive(Clone)]
pub(crate) struct AnyValue {
inner: std::sync::Arc<dyn std::any::Any + Send + Sync + 'static>,
// While we can extract `TypeId` from `inner`, the debug repr is of a number, so let's track
// the type_name in debug builds.
id: AnyValueId,
}
impl AnyValue {
pub(crate) fn new<V: std::any::Any + Clone + Send + Sync + 'static>(inner: V) -> Self {
let id = AnyValueId::of::<V>();
let inner = std::sync::Arc::new(inner);
Self { inner, id }
}
pub(crate) fn downcast_ref<T: std::any::Any + Clone + Send + Sync + 'static>(
&self,
) -> Option<&T> {
self.inner.downcast_ref::<T>()
}
pub(crate) fn downcast_into<T: std::any::Any + Clone + Send + Sync>(self) -> Result<T, Self> {
let id = self.id;
let value =
ok!(std::sync::Arc::downcast::<T>(self.inner).map_err(|inner| Self { inner, id }));
let value = std::sync::Arc::try_unwrap(value).unwrap_or_else(|arc| (*arc).clone());
Ok(value)
}
pub(crate) fn type_id(&self) -> AnyValueId {
self.id
}
}
impl std::fmt::Debug for AnyValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("AnyValue").field("inner", &self.id).finish()
}
}
#[derive(Copy, Clone)]
pub struct AnyValueId {
type_id: std::any::TypeId,
#[cfg(debug_assertions)]
type_name: &'static str,
}
impl AnyValueId {
pub(crate) fn of<A: ?Sized + 'static>() -> Self {
Self {
type_id: std::any::TypeId::of::<A>(),
#[cfg(debug_assertions)]
type_name: std::any::type_name::<A>(),
}
}
}
impl PartialEq for AnyValueId {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id
}
}
impl Eq for AnyValueId {}
impl PartialOrd for AnyValueId {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq<std::any::TypeId> for AnyValueId {
fn eq(&self, other: &std::any::TypeId) -> bool {
self.type_id == *other
}
}
impl Ord for AnyValueId {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.type_id.cmp(&other.type_id)
}
}
impl std::hash::Hash for AnyValueId {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.type_id.hash(state);
}
}
impl std::fmt::Debug for AnyValueId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
#[cfg(not(debug_assertions))]
{
self.type_id.fmt(f)
}
#[cfg(debug_assertions)]
{
f.debug_struct(self.type_name).finish()
}
}
}
impl<'a, A: ?Sized + 'static> From<&'a A> for AnyValueId {
fn from(_: &'a A) -> Self {
Self::of::<A>()
}
}
#[cfg(test)]
mod test {
#[test]
#[cfg(debug_assertions)]
fn debug_impl() {
use super::*;
assert_eq!(format!("{:?}", AnyValue::new(5)), "AnyValue { inner: i32 }");
}
#[test]
fn eq_to_type_id() {
use super::*;
let any_value_id = AnyValueId::of::<i32>();
let type_id = std::any::TypeId::of::<i32>();
assert_eq!(any_value_id, type_id);
}
}

116
vendor/clap_builder/src/util/color.rs vendored Normal file
View File

@ -0,0 +1,116 @@
use crate::builder::PossibleValue;
use crate::derive::ValueEnum;
/// Represents the color preferences for program output
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ColorChoice {
/// Enables colored output only when the output is going to a terminal or TTY.
///
/// **NOTE:** This is the default behavior of `clap`.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms).
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "color")] {
/// # use clap_builder as clap;
/// # use clap::{Command, ColorChoice};
/// Command::new("myprog")
/// .color(ColorChoice::Auto)
/// .get_matches();
/// # }
/// ```
Auto,
/// Enables colored output regardless of whether or not the output is going to a terminal/TTY.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms).
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "color")] {
/// # use clap_builder as clap;
/// # use clap::{Command, ColorChoice};
/// Command::new("myprog")
/// .color(ColorChoice::Always)
/// .get_matches();
/// # }
/// ```
Always,
/// Disables colored output no matter if the output is going to a terminal/TTY, or not.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms)
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "color")] {
/// # use clap_builder as clap;
/// # use clap::{Command, ColorChoice};
/// Command::new("myprog")
/// .color(ColorChoice::Never)
/// .get_matches();
/// # }
/// ```
Never,
}
impl ColorChoice {
/// Report all `possible_values`
pub fn possible_values() -> impl Iterator<Item = PossibleValue> {
Self::value_variants()
.iter()
.filter_map(ValueEnum::to_possible_value)
}
}
impl Default for ColorChoice {
fn default() -> Self {
Self::Auto
}
}
impl std::fmt::Display for ColorChoice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
impl std::str::FromStr for ColorChoice {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for variant in Self::value_variants() {
if variant.to_possible_value().unwrap().matches(s, false) {
return Ok(*variant);
}
}
Err(format!("invalid variant: {s}"))
}
}
impl ValueEnum for ColorChoice {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Auto, Self::Always, Self::Never]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
Some(match self {
Self::Auto => PossibleValue::new("auto"),
Self::Always => PossibleValue::new("always"),
Self::Never => PossibleValue::new("never"),
})
}
}

254
vendor/clap_builder/src/util/flat_map.rs vendored Normal file
View File

@ -0,0 +1,254 @@
#![allow(dead_code)]
use std::borrow::Borrow;
/// Flat (Vec) backed map
///
/// This preserves insertion order
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct FlatMap<K, V> {
keys: Vec<K>,
values: Vec<V>,
}
impl<K: PartialEq + Eq, V> FlatMap<K, V> {
pub(crate) fn new() -> Self {
Default::default()
}
pub(crate) fn insert(&mut self, key: K, mut value: V) -> Option<V> {
for (index, existing) in self.keys.iter().enumerate() {
if *existing == key {
std::mem::swap(&mut self.values[index], &mut value);
return Some(value);
}
}
self.insert_unchecked(key, value);
None
}
pub(crate) fn insert_unchecked(&mut self, key: K, value: V) {
self.keys.push(key);
self.values.push(value);
}
pub(crate) fn extend_unchecked(&mut self, iter: impl IntoIterator<Item = (K, V)>) {
for (key, value) in iter {
self.insert_unchecked(key, value);
}
}
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Eq,
{
for existing in &self.keys {
if existing.borrow() == key {
return true;
}
}
false
}
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: std::hash::Hash + Eq,
{
self.remove_entry(key).map(|(_, v)| v)
}
pub fn remove_entry<Q: ?Sized>(&mut self, key: &Q) -> Option<(K, V)>
where
K: Borrow<Q>,
Q: std::hash::Hash + Eq,
{
let index = some!(self
.keys
.iter()
.enumerate()
.find_map(|(i, k)| (k.borrow() == key).then_some(i)));
let key = self.keys.remove(index);
let value = self.values.remove(index);
Some((key, value))
}
pub(crate) fn is_empty(&self) -> bool {
self.keys.is_empty()
}
pub fn entry(&mut self, key: K) -> Entry<K, V> {
for (index, existing) in self.keys.iter().enumerate() {
if *existing == key {
return Entry::Occupied(OccupiedEntry { v: self, index });
}
}
Entry::Vacant(VacantEntry { v: self, key })
}
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Eq,
{
for (index, existing) in self.keys.iter().enumerate() {
if existing.borrow() == k {
return Some(&self.values[index]);
}
}
None
}
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Eq,
{
for (index, existing) in self.keys.iter().enumerate() {
if existing.borrow() == k {
return Some(&mut self.values[index]);
}
}
None
}
pub fn keys(&self) -> std::slice::Iter<'_, K> {
self.keys.iter()
}
pub fn iter(&self) -> Iter<K, V> {
Iter {
keys: self.keys.iter(),
values: self.values.iter(),
}
}
pub fn iter_mut(&mut self) -> IterMut<K, V> {
IterMut {
keys: self.keys.iter_mut(),
values: self.values.iter_mut(),
}
}
}
impl<K: PartialEq + Eq, V> Default for FlatMap<K, V> {
fn default() -> Self {
Self {
keys: Default::default(),
values: Default::default(),
}
}
}
pub enum Entry<'a, K: 'a, V: 'a> {
Vacant(VacantEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V>),
}
impl<'a, K: 'a, V: 'a> Entry<'a, K, V> {
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
Entry::Occupied(entry) => &mut entry.v.values[entry.index],
Entry::Vacant(entry) => {
entry.v.keys.push(entry.key);
entry.v.values.push(default);
entry.v.values.last_mut().unwrap()
}
}
}
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self {
Entry::Occupied(entry) => &mut entry.v.values[entry.index],
Entry::Vacant(entry) => {
entry.v.keys.push(entry.key);
entry.v.values.push(default());
entry.v.values.last_mut().unwrap()
}
}
}
}
pub struct VacantEntry<'a, K: 'a, V: 'a> {
v: &'a mut FlatMap<K, V>,
key: K,
}
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
v: &'a mut FlatMap<K, V>,
index: usize,
}
pub struct Iter<'a, K: 'a, V: 'a> {
keys: std::slice::Iter<'a, K>,
values: std::slice::Iter<'a, V>,
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<(&'a K, &'a V)> {
match self.keys.next() {
Some(k) => {
let v = self.values.next().unwrap();
Some((k, v))
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.keys.size_hint()
}
}
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
match self.keys.next_back() {
Some(k) => {
let v = self.values.next_back().unwrap();
Some((k, v))
}
None => None,
}
}
}
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {}
pub struct IterMut<'a, K: 'a, V: 'a> {
keys: std::slice::IterMut<'a, K>,
values: std::slice::IterMut<'a, V>,
}
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
type Item = (&'a K, &'a mut V);
fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
match self.keys.next() {
Some(k) => {
let v = self.values.next().unwrap();
Some((k, v))
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.keys.size_hint()
}
}
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
match self.keys.next_back() {
Some(k) => {
let v = self.values.next_back().unwrap();
Some((k, v))
}
None => None,
}
}
}
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {}

107
vendor/clap_builder/src/util/flat_set.rs vendored Normal file
View File

@ -0,0 +1,107 @@
#![allow(dead_code)]
use std::borrow::Borrow;
/// Flat (Vec) backed set
///
/// This preserves insertion order
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct FlatSet<T> {
inner: Vec<T>,
}
impl<T: PartialEq + Eq> FlatSet<T> {
pub(crate) fn new() -> Self {
Default::default()
}
pub(crate) fn insert(&mut self, value: T) -> bool {
for existing in &self.inner {
if *existing == value {
return false;
}
}
self.inner.push(value);
true
}
pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
where
T: Borrow<Q>,
Q: Eq,
{
for existing in &self.inner {
if existing.borrow() == value {
return true;
}
}
false
}
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(&T) -> bool,
{
self.inner.retain(f);
}
pub(crate) fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub(crate) fn iter(&self) -> std::slice::Iter<'_, T> {
self.inner.iter()
}
pub fn sort_by_key<K, F>(&mut self, f: F)
where
F: FnMut(&T) -> K,
K: Ord,
{
self.inner.sort_by_key(f);
}
}
impl<T: PartialEq + Eq> Default for FlatSet<T> {
fn default() -> Self {
Self {
inner: Default::default(),
}
}
}
impl<T: PartialEq + Eq> IntoIterator for FlatSet<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'s, T: PartialEq + Eq> IntoIterator for &'s FlatSet<T> {
type Item = &'s T;
type IntoIter = std::slice::Iter<'s, T>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
impl<T: PartialEq + Eq> Extend<T> for FlatSet<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for value in iter {
self.insert(value);
}
}
}
impl<T: PartialEq + Eq> FromIterator<T> for FlatSet<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut set = Self::new();
for value in iter {
set.insert(value);
}
set
}
}

49
vendor/clap_builder/src/util/graph.rs vendored Normal file
View File

@ -0,0 +1,49 @@
#[derive(Debug)]
struct Child<T> {
id: T,
children: Vec<usize>,
}
impl<T> Child<T> {
fn new(id: T) -> Self {
Child {
id,
children: vec![],
}
}
}
#[derive(Debug)]
pub(crate) struct ChildGraph<T>(Vec<Child<T>>);
impl<T> ChildGraph<T>
where
T: Sized + PartialEq + Clone,
{
pub(crate) fn with_capacity(s: usize) -> Self {
ChildGraph(Vec::with_capacity(s))
}
pub(crate) fn insert(&mut self, req: T) -> usize {
self.0.iter().position(|e| e.id == req).unwrap_or_else(|| {
let idx = self.0.len();
self.0.push(Child::new(req));
idx
})
}
pub(crate) fn insert_child(&mut self, parent: usize, child: T) -> usize {
let c_idx = self.0.len();
self.0.push(Child::new(child));
self.0[parent].children.push(c_idx);
c_idx
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &T> {
self.0.iter().map(|r| &r.id)
}
pub(crate) fn contains(&self, req: &T) -> bool {
self.0.iter().any(|r| r.id == *req)
}
}

164
vendor/clap_builder/src/util/id.rs vendored Normal file
View File

@ -0,0 +1,164 @@
use crate::builder::Str;
/// [`Arg`][crate::Arg] or [`ArgGroup`][crate::ArgGroup] identifier
///
/// This is used for accessing the value in [`ArgMatches`][crate::ArgMatches] or defining
/// relationships between `Arg`s and `ArgGroup`s with functions like
/// [`Arg::conflicts_with`][crate::Arg::conflicts_with].
#[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Id(Str);
impl Id {
pub(crate) const HELP: &'static str = "help";
pub(crate) const VERSION: &'static str = "version";
pub(crate) const EXTERNAL: &'static str = "";
pub(crate) fn from_static_ref(name: &'static str) -> Self {
Self(Str::from_static_ref(name))
}
/// Get the raw string of the `Id`
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub(crate) fn as_internal_str(&self) -> &Str {
&self.0
}
}
impl From<&'_ Id> for Id {
fn from(id: &'_ Id) -> Self {
id.clone()
}
}
impl From<Str> for Id {
fn from(name: Str) -> Self {
Self(name)
}
}
impl From<&'_ Str> for Id {
fn from(name: &'_ Str) -> Self {
Self(name.into())
}
}
#[cfg(feature = "string")]
impl From<std::string::String> for Id {
fn from(name: std::string::String) -> Self {
Self(name.into())
}
}
#[cfg(feature = "string")]
impl From<&'_ std::string::String> for Id {
fn from(name: &'_ std::string::String) -> Self {
Self(name.into())
}
}
impl From<&'static str> for Id {
fn from(name: &'static str) -> Self {
Self(name.into())
}
}
impl From<&'_ &'static str> for Id {
fn from(name: &'_ &'static str) -> Self {
Self(name.into())
}
}
impl From<Id> for Str {
fn from(name: Id) -> Self {
name.0
}
}
impl From<Id> for String {
fn from(name: Id) -> Self {
Str::from(name).into()
}
}
impl std::fmt::Display for Id {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.as_str(), f)
}
}
impl std::fmt::Debug for Id {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self.as_str(), f)
}
}
impl AsRef<str> for Id {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl std::borrow::Borrow<str> for Id {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl PartialEq<str> for Id {
#[inline]
fn eq(&self, other: &str) -> bool {
PartialEq::eq(self.as_str(), other)
}
}
impl PartialEq<Id> for str {
#[inline]
fn eq(&self, other: &Id) -> bool {
PartialEq::eq(self, other.as_str())
}
}
impl PartialEq<&'_ str> for Id {
#[inline]
fn eq(&self, other: &&str) -> bool {
PartialEq::eq(self.as_str(), *other)
}
}
impl PartialEq<Id> for &'_ str {
#[inline]
fn eq(&self, other: &Id) -> bool {
PartialEq::eq(*self, other.as_str())
}
}
impl PartialEq<Str> for Id {
#[inline]
fn eq(&self, other: &Str) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
impl PartialEq<Id> for Str {
#[inline]
fn eq(&self, other: &Id) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
impl PartialEq<std::string::String> for Id {
#[inline]
fn eq(&self, other: &std::string::String) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
impl PartialEq<Id> for std::string::String {
#[inline]
fn eq(&self, other: &Id) -> bool {
PartialEq::eq(other, self)
}
}

47
vendor/clap_builder/src/util/mod.rs vendored Normal file
View File

@ -0,0 +1,47 @@
#![allow(clippy::single_component_path_imports)]
mod any_value;
pub(crate) mod flat_map;
pub(crate) mod flat_set;
mod graph;
mod id;
mod str_to_bool;
pub use self::id::Id;
pub(crate) use self::any_value::AnyValue;
pub(crate) use self::any_value::AnyValueId;
pub(crate) use self::flat_map::Entry;
pub(crate) use self::flat_map::FlatMap;
pub(crate) use self::flat_set::FlatSet;
pub(crate) use self::graph::ChildGraph;
pub(crate) use self::str_to_bool::str_to_bool;
pub(crate) use self::str_to_bool::FALSE_LITERALS;
pub(crate) use self::str_to_bool::TRUE_LITERALS;
pub(crate) mod color;
pub(crate) const SUCCESS_CODE: i32 = 0;
// While sysexists.h defines EX_USAGE as 64, this doesn't seem to be used much in practice but
// instead 2 seems to be frequently used.
// Examples
// - GNU `ls` returns 2
// - Python's `argparse` returns 2
pub(crate) const USAGE_CODE: i32 = 2;
pub(crate) fn safe_exit(code: i32) -> ! {
use std::io::Write;
let _ = std::io::stdout().lock().flush();
let _ = std::io::stderr().lock().flush();
std::process::exit(code)
}
#[cfg(not(feature = "unicode"))]
pub(crate) fn eq_ignore_case(left: &str, right: &str) -> bool {
left.eq_ignore_ascii_case(right)
}
#[cfg(feature = "unicode")]
pub(crate) use unicase::eq as eq_ignore_case;

View File

@ -0,0 +1,21 @@
/// True values are `y`, `yes`, `t`, `true`, `on`, and `1`.
pub(crate) const TRUE_LITERALS: [&str; 6] = ["y", "yes", "t", "true", "on", "1"];
/// False values are `n`, `no`, `f`, `false`, `off`, and `0`.
pub(crate) const FALSE_LITERALS: [&str; 6] = ["n", "no", "f", "false", "off", "0"];
/// Converts a string literal representation of truth to true or false.
///
/// `false` values are `n`, `no`, `f`, `false`, `off`, and `0` (case insensitive).
///
/// Any other value will be considered as `true`.
pub(crate) fn str_to_bool(val: impl AsRef<str>) -> Option<bool> {
let pat: &str = &val.as_ref().to_lowercase();
if TRUE_LITERALS.contains(&pat) {
Some(true)
} else if FALSE_LITERALS.contains(&pat) {
Some(false)
} else {
None
}
}