chore(vendor): add anyhow crate
This commit was merged in pull request #68.
This commit is contained in:
114
third_party/crates/clap_builder-4.6.7/src/error/context.rs
vendored
Normal file
114
third_party/crates/clap_builder-4.6.7/src/error/context.rs
vendored
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
494
third_party/crates/clap_builder-4.6.7/src/error/format.rs
vendored
Normal file
494
third_party/crates/clap_builder-4.6.7/src/error/format.rs
vendored
Normal file
@@ -0,0 +1,494 @@
|
||||
#![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 std::borrow::Cow;
|
||||
|
||||
use crate::ArgAction;
|
||||
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;
|
||||
use crate::util::Escape;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **NOTE:** Consider removing the `error-context` default feature if using this to remove all
|
||||
/// overhead for [`RichFormatter`].
|
||||
///
|
||||
/// </div>
|
||||
#[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}{valid}tip:{valid:#} ",);
|
||||
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.as_deref());
|
||||
|
||||
styled
|
||||
}
|
||||
}
|
||||
|
||||
fn start_error(styled: &mut StyledStr, styles: &Styles) {
|
||||
use std::fmt::Write as _;
|
||||
let error = &styles.get_error();
|
||||
let _ = write!(styled, "{error}error:{error:#} ");
|
||||
}
|
||||
|
||||
#[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 mut prior_arg = error.get(ContextKind::PriorArg);
|
||||
if let Some(ContextValue::String(invalid_arg)) = error.get(ContextKind::InvalidArg) {
|
||||
if Some(&ContextValue::String(invalid_arg.clone())) == prior_arg {
|
||||
prior_arg = None;
|
||||
let _ = write!(
|
||||
styled,
|
||||
"the argument '{invalid}{invalid_arg}{invalid:#}' cannot be used multiple times",
|
||||
);
|
||||
} else {
|
||||
let _ = write!(
|
||||
styled,
|
||||
"the argument '{invalid}{invalid_arg}{invalid:#}' cannot be used with",
|
||||
);
|
||||
}
|
||||
} else if let Some(ContextValue::String(invalid_arg)) =
|
||||
error.get(ContextKind::InvalidSubcommand)
|
||||
{
|
||||
let _ = write!(
|
||||
styled,
|
||||
"the subcommand '{invalid}{invalid_arg}{invalid:#}' cannot be used with",
|
||||
);
|
||||
} else {
|
||||
styled.push_str(error.kind().as_str().unwrap());
|
||||
}
|
||||
|
||||
if let Some(prior_arg) = prior_arg {
|
||||
match prior_arg {
|
||||
ContextValue::Strings(values) => {
|
||||
styled.push_str(":");
|
||||
for v in values {
|
||||
let _ = write!(styled, "\n{TAB}{invalid}{v}{invalid:#}",);
|
||||
}
|
||||
}
|
||||
ContextValue::String(value) => {
|
||||
let _ = write!(styled, " '{invalid}{value}{invalid:#}'",);
|
||||
}
|
||||
_ => {
|
||||
styled.push_str(" one or more of the other specified arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
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}{invalid_arg}{invalid:#}'",
|
||||
);
|
||||
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}{invalid_arg}{invalid:#}' but none was supplied",
|
||||
);
|
||||
} else {
|
||||
let _ = write!(
|
||||
styled,
|
||||
"invalid value '{invalid}{invalid_value}{invalid:#}' for '{literal}{invalid_arg}{literal:#}'",
|
||||
);
|
||||
}
|
||||
|
||||
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}{invalid_sub}{invalid:#}'",
|
||||
);
|
||||
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}{valid}{v}{valid:#}",);
|
||||
}
|
||||
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}{invalid_sub}{invalid:#}' requires a subcommand but one was not provided",
|
||||
);
|
||||
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}{invalid_value}{invalid:#}' for '{literal}{invalid_arg}{literal:#}' found; no more were expected",
|
||||
);
|
||||
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,
|
||||
"{valid}{min_values}{valid:#} values required by '{literal}{invalid_arg}{literal:#}'; only {invalid}{actual_num_values}{invalid:#}{were_provided}",
|
||||
);
|
||||
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}{invalid_value}{invalid:#}' for '{literal}{invalid_arg}{literal:#}'",
|
||||
);
|
||||
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,
|
||||
"{valid}{num_values}{valid:#} values required for '{literal}{invalid_arg}{literal:#}' but {invalid}{actual_num_values}{invalid:#}{were_provided}",
|
||||
);
|
||||
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}{invalid_arg}{invalid:#}' found",
|
||||
);
|
||||
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}: ");
|
||||
|
||||
for (idx, val) in possible_values.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
styled.push_str(", ");
|
||||
}
|
||||
let _ = write!(styled, "{valid}{}{valid:#}", 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).as_deref());
|
||||
}
|
||||
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<Cow<'static, str>> {
|
||||
if !cmd.is_disable_help_flag_set() {
|
||||
Some(Cow::Borrowed("--help"))
|
||||
} else if let Some(flag) = get_user_help_flag(cmd) {
|
||||
Some(Cow::Owned(flag))
|
||||
} else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() {
|
||||
Some(Cow::Borrowed("help"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_user_help_flag(cmd: &Command) -> Option<String> {
|
||||
let arg = cmd.get_arguments().find(|arg| match arg.get_action() {
|
||||
ArgAction::Help | ArgAction::HelpShort | ArgAction::HelpLong => true,
|
||||
ArgAction::Append
|
||||
| ArgAction::Count
|
||||
| ArgAction::SetTrue
|
||||
| ArgAction::SetFalse
|
||||
| ArgAction::Set
|
||||
| ArgAction::Version => false,
|
||||
})?;
|
||||
|
||||
arg.get_long()
|
||||
.map(|long| format!("--{long}"))
|
||||
.or_else(|| arg.get_short().map(|short| format!("-{short}")))
|
||||
}
|
||||
|
||||
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 '{literal}{help}{literal:#}'.\n",
|
||||
);
|
||||
} else {
|
||||
styled.push_str("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "error-context")]
|
||||
fn did_you_mean(styled: &mut StyledStr, styles: &Styles, context: &str, possibles: &ContextValue) {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
let valid = &styles.get_valid();
|
||||
let _ = write!(styled, "{TAB}{valid}tip:{valid:#}",);
|
||||
if let ContextValue::String(possible) = possibles {
|
||||
let _ = write!(
|
||||
styled,
|
||||
" a similar {context} exists: '{valid}{possible}{valid:#}'",
|
||||
);
|
||||
} else if let ContextValue::Strings(possibles) = possibles {
|
||||
if possibles.len() == 1 {
|
||||
let _ = write!(styled, " a similar {context} exists: ",);
|
||||
} else {
|
||||
let _ = write!(styled, " some similar {context}s exist: ",);
|
||||
}
|
||||
for (i, possible) in possibles.iter().enumerate() {
|
||||
if i != 0 {
|
||||
styled.push_str(", ");
|
||||
}
|
||||
let _ = write!(styled, "'{valid}{possible}{valid:#}'",);
|
||||
}
|
||||
}
|
||||
}
|
||||
365
third_party/crates/clap_builder-4.6.7/src/error/kind.rs
vendored
Normal file
365
third_party/crates/clap_builder-4.6.7/src/error/kind.rs
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
/// 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))`][crate::Arg::value_parser] 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);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`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)
|
||||
}
|
||||
}
|
||||
946
third_party/crates/clap_builder-4.6.7/src/error/mod.rs
vendored
Normal file
946
third_party/crates/clap_builder-4.6.7/src/error/mod.rs
vendored
Normal file
@@ -0,0 +1,946 @@
|
||||
//! 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,
|
||||
result::Result as StdResult,
|
||||
};
|
||||
|
||||
// Internal
|
||||
use crate::Command;
|
||||
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::{SUCCESS_CODE, USAGE_CODE, color::ColorChoice};
|
||||
|
||||
#[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<Cow<'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 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
|
||||
///
|
||||
/// If this `ContextKind` is already present, its value is replaced and the old value is returned.
|
||||
#[inline(never)]
|
||||
#[cfg(feature = "error-context")]
|
||||
pub fn insert(&mut self, kind: ContextKind, value: ContextValue) -> Option<ContextValue> {
|
||||
self.inner.context.insert(kind, value)
|
||||
}
|
||||
|
||||
/// Remove a piece of context, return the old value if any
|
||||
///
|
||||
/// The context is currently implemented in a vector, so `remove` takes
|
||||
/// linear time.
|
||||
#[inline(never)]
|
||||
#[cfg(feature = "error-context")]
|
||||
pub fn remove(&mut self, kind: ContextKind) -> Option<ContextValue> {
|
||||
self.inner.context.remove(&kind)
|
||||
}
|
||||
|
||||
/// 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();
|
||||
std::process::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<Cow<'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 subcommand_conflict(
|
||||
cmd: &Command,
|
||||
sub: 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::InvalidSubcommand, ContextValue::String(sub)),
|
||||
(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).clone()).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 '{invalid}{subcmd}{invalid:#}' as a value, use '{valid}{name} -- {subcmd}{valid:#}'",
|
||||
);
|
||||
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 '{invalid}{arg}{invalid:#}' as a value, use '{valid}-- {arg}{valid:#}'",
|
||||
);
|
||||
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, "'{valid}{sub} {flag}{valid:#}' exists",);
|
||||
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 '{valid}{arg}{valid:#}' exists; to use it, remove the '{invalid}--{invalid:#}' before it",
|
||||
);
|
||||
|
||||
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 {
|
||||
Self::raw(ErrorKind::Io, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: ErrorFormatter> From<fmt::Error> for Error<F> {
|
||||
fn from(e: fmt::Error) -> Self {
|
||||
Self::raw(ErrorKind::Format, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: ErrorFormatter> Debug for Error<F> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), 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 {
|
||||
Self::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);
|
||||
}
|
||||
Self::Formatted(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn formatted(&self, styles: &Styles) -> Cow<'_, StyledStr> {
|
||||
match self {
|
||||
Self::Raw(s) => {
|
||||
let styled = format::format_error_message(s, styles, None, None);
|
||||
|
||||
Cow::Owned(styled)
|
||||
}
|
||||
Self::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);
|
||||
}
|
||||
Reference in New Issue
Block a user