// Std
use std::{
convert::From,
error,
fmt::{self, Debug, Display, Formatter},
io,
result::Result as StdResult,
};
// Internal
use crate::{
build::Arg,
output::fmt::Colorizer,
parse::features::suggestions,
util::{safe_exit, termcolor::ColorChoice},
};
/// Short hand for [`Result`] type
///
/// [`Result`]: std::result::Result
pub type Result<T> = StdResult<T, Error>;
/// Command line argument parser kind of error
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ErrorKind {
/// Occurs when an [`Arg`] has a set of possible values,
/// and the user provides a value which isn't in that set.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("speed")
/// .possible_value("fast")
/// .possible_value("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
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::from("--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
///
#[cfg_attr(not(feature = "suggestions"), doc = " ```no_run")]
#[cfg_attr(feature = "suggestions", doc = " ```")]
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind, };
/// let result = App::new("prog")
/// .subcommand(App::new("config")
/// .about("Used for configuration")
/// .arg(Arg::new("config_file")
/// .about("The configuration file to use")
/// .index(1)))
/// .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 provides an unrecognized [`Subcommand`] which either
/// doesn't meet the threshold for being similar enough to an existing subcommand,
/// or the 'suggestions' feature is disabled.
/// Otherwise the more detailed [`InvalidSubcommand`] error is returned.
///
/// This error typically happens when passing additional subcommand names to the `help`
/// subcommand. Otherwise, the more general [`UnknownArgument`] error is used.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind, };
/// let result = App::new("prog")
/// .subcommand(App::new("config")
/// .about("Used for configuration")
/// .arg(Arg::new("config_file")
/// .about("The configuration file to use")
/// .index(1)))
/// .try_get_matches_from(vec!["prog", "help", "nothing"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::UnrecognizedSubcommand);
/// ```
///
/// [`Subcommand`]: crate::Subcommand
/// [`InvalidSubcommand`]: ErrorKind::InvalidSubcommand
/// [`UnknownArgument`]: ErrorKind::UnknownArgument
UnrecognizedSubcommand,
/// Occurs when the user provides an empty value for an option that does not allow empty
/// values.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind, ArgSettings};
/// let res = App::new("prog")
/// .arg(Arg::new("color")
/// .setting(ArgSettings::TakesValue)
/// .setting(ArgSettings::ForbidEmptyValues)
/// .long("color"))
/// .try_get_matches_from(vec!["prog", "--color="]);
/// assert!(res.is_err());
/// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
/// ```
EmptyValue,
/// Occurs when the user doesn't use equals for an option that requres equal
/// sign to provide values.
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind, ArgSettings};
/// let res = App::new("prog")
/// .arg(Arg::new("color")
/// .setting(ArgSettings::TakesValue)
/// .setting(ArgSettings::RequireEquals)
/// .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
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// fn is_numeric(val: &str) -> Result<(), String> {
/// match val.parse::<i64>() {
/// Ok(..) => Ok(()),
/// Err(..) => Err(String::from("Value wasn't a number!")),
/// }
/// }
///
/// let result = App::new("prog")
/// .arg(Arg::new("num")
/// .validator(is_numeric))
/// .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::max_values`].
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("arg")
/// .max_values(2))
/// .try_get_matches_from(vec!["prog", "too", "many", "values"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::TooManyValues);
/// ```
/// [`Arg::max_values`]: Arg::max_values()
TooManyValues,
/// Occurs when the user provides fewer values for an argument than were defined by setting
/// [`Arg::min_values`].
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("some_opt")
/// .long("opt")
/// .min_values(3))
/// .try_get_matches_from(vec!["prog", "--opt", "too", "few"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::TooFewValues);
/// ```
/// [`Arg::min_values`]: Arg::min_values()
TooFewValues,
/// Occurs when a user provides more occurrences for an argument than were defined by setting
/// [`Arg::max_occurrences`].
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("verbosity")
/// .short('v')
/// .max_occurrences(2))
/// .try_get_matches_from(vec!["prog", "-vvv"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::TooManyOccurrences);
/// ```
/// [`Arg::max_occurrences`]: Arg::max_occurrences()
TooManyOccurrences,
/// Occurs when the user provides a different number of values for an argument than what's
/// been defined by setting [`Arg::number_of_values`] or than was implicitly set by
/// [`Arg::value_names`].
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("some_opt")
/// .long("opt")
/// .takes_value(true)
/// .number_of_values(2))
/// .try_get_matches_from(vec!["prog", "--opt", "wrong"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::WrongNumberOfValues);
/// ```
///
/// [`Arg::number_of_values`]: Arg::number_of_values()
/// [`Arg::value_names`]: Arg::value_names()
WrongNumberOfValues,
/// Occurs when the user provides two values which conflict with each other and can't be used
/// together.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("debug")
/// .long("debug")
/// .conflicts_with("color"))
/// .arg(Arg::new("color")
/// .long("color"))
/// .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
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::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 [`AppSettings::SubcommandRequired`]),
/// but the user does not provide one.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, AppSettings, ErrorKind};
/// let err = App::new("prog")
/// .setting(AppSettings::SubcommandRequired)
/// .subcommand(App::new("test"))
/// .try_get_matches_from(vec![
/// "myprog",
/// ]);
/// assert!(err.is_err());
/// assert_eq!(err.unwrap_err().kind, ErrorKind::MissingSubcommand);
/// # ;
/// ```
///
/// [`AppSettings::SubcommandRequired`]: crate::AppSettings::SubcommandRequired
MissingSubcommand,
/// Occurs when the user provides multiple values to an argument which doesn't allow that.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .arg(Arg::new("debug")
/// .long("debug")
/// .multiple_occurrences(false))
/// .try_get_matches_from(vec!["prog", "--debug", "--debug"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage);
/// ```
UnexpectedMultipleUsage,
/// Occurs when the user provides a value containing invalid UTF-8 for an argument and
/// [`AppSettings::StrictUtf8`] is set.
///
/// # Platform Specific
///
/// Non-Windows platforms only (such as Linux, Unix, OSX, etc.)
///
/// # Examples
///
#[cfg_attr(not(unix), doc = " ```ignore")]
#[cfg_attr(unix, doc = " ```")]
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind, AppSettings};
/// # use std::os::unix::ffi::OsStringExt;
/// # use std::ffi::OsString;
/// let result = App::new("prog")
/// .setting(AppSettings::StrictUtf8)
/// .arg(Arg::new("utf8")
/// .short('u')
/// .takes_value(true))
/// .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);
/// ```
///
/// [`AppSettings::StrictUtf8`]: crate::AppSettings::StrictUtf8
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
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::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
/// [`AppSettings::ArgRequiredElseHelp`] and
/// [`AppSettings::SubcommandRequiredElseHelp`], but the user did not provide
/// one.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, AppSettings, ErrorKind, };
/// let result = App::new("prog")
/// .setting(AppSettings::ArgRequiredElseHelp)
/// .subcommand(App::new("config")
/// .about("Used for configuration")
/// .arg(Arg::new("config_file")
/// .about("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
/// [`AppSettings::ArgRequiredElseHelp`]: crate::AppSettings::ArgRequiredElseHelp
/// [`AppSettings::SubcommandRequiredElseHelp`]: crate::AppSettings::SubcommandRequiredElseHelp
DisplayHelpOnMissingArgumentOrSubcommand,
/// Not a true "error" as it means `--version` or similar was used.
/// The message will be sent to `stdout`.
///
/// # Examples
///
/// ```rust
/// # extern crate nameless_clap as clap;
/// # use clap::{App, Arg, ErrorKind};
/// let result = App::new("prog")
/// .try_get_matches_from(vec!["prog", "--version"]);
/// assert!(result.is_err());
/// assert_eq!(result.unwrap_err().kind, ErrorKind::DisplayVersion);
/// ```
DisplayVersion,
/// Occurs when using the [`ArgMatches::value_of_t`] and friends to convert an argument value
/// into type `T`, but the argument you requested wasn't used. I.e. you asked for an argument
/// with name `config` to be converted, but `config` wasn't used by the user.
///
/// [`ArgMatches::value_of_t`]: crate::ArgMatches::value_of_t()
ArgumentNotFound,
/// 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,
}
/// Command Line Argument Parser Error
#[derive(Debug)]
pub struct Error {
/// Formatted error message, enhancing the cause message with extra information
pub(crate) message: Colorizer,
/// The type of error
pub kind: ErrorKind,
/// Additional information depending on the error kind, like values and argument names.
/// Useful when you want to render an error of your own.
pub info: Vec<String>,
pub(crate) source: Option<Box<dyn error::Error>>,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.message, f)
}
}
fn start_error(c: &mut Colorizer, msg: impl Into<String>) {
c.error("error:");
c.none(" ");
c.none(msg);
}
fn put_usage(c: &mut Colorizer, usage: impl Into<String>) {
c.none("\n\n");
c.none(usage);
}
fn try_help(c: &mut Colorizer) {
c.none("\n\nFor more information try ");
c.good("--help");
c.none("\n");
}
impl Error {
/// Returns the singular or plural form on the verb to be based on the argument's value.
fn singular_or_plural(n: usize) -> String {
if n > 1 {
String::from("were")
} else {
String::from("was")
}
}
/// Should the message be written to `stdout` or not
#[inline]
pub fn use_stderr(&self) -> bool {
!matches!(
self.kind,
ErrorKind::DisplayHelp
| ErrorKind::DisplayVersion
| ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
)
}
/// Prints the error and exits. Depending on the error kind, this
/// either prints to `stderr` and exits with a status of `1`
/// or prints to `stdout` and exits with a status of `0`.
pub fn exit(&self) -> ! {
if self.use_stderr() {
self.message.print().expect("Error writing Error to stderr");
safe_exit(1);
}
self.message.print().expect("Error writing Error to stdout");
safe_exit(0)
}
/// Prints formatted and colored error to `stdout` or `stderr` according to its error kind
///
/// # Example
/// ```no_run
/// # extern crate nameless_clap as clap;
/// use clap::App;
///
/// match App::new("App").try_get_matches() {
/// Ok(matches) => {
/// // do_something
/// },
/// Err(err) => {
/// err.print().expect("Error writing Error");
/// // do_something
/// },
/// };
/// ```
pub fn print(&self) -> io::Result<()> {
self.message.print()
}
pub(crate) fn argument_conflict(
arg: &Arg,
other: Option<String>,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
let arg = arg.to_string();
start_error(&mut c, "The argument '");
c.warning(arg.clone());
c.none("' cannot be used with ");
match other {
Some(ref name) => {
c.none("'");
c.warning(name);
c.none("'");
}
None => {
c.none("one or more of the other specified arguments");
}
};
put_usage(&mut c, usage);
try_help(&mut c);
let mut info = vec![arg];
if let Some(other) = other {
info.push(other);
}
Error {
message: c,
kind: ErrorKind::ArgumentConflict,
info,
source: None,
}
}
pub(crate) fn empty_value(arg: &Arg, usage: String, color: ColorChoice) -> Self {
let mut c = Colorizer::new(true, color);
let arg = arg.to_string();
start_error(&mut c, "The argument '");
c.warning(arg.clone());
c.none("' requires a value but none was supplied");
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::EmptyValue,
info: vec![arg],
source: None,
}
}
pub(crate) fn no_equals(arg: &Arg, usage: String, color: ColorChoice) -> Self {
let mut c = Colorizer::new(true, color);
let arg = arg.to_string();
start_error(&mut c, "Equal sign is needed when assigning values to '");
c.warning(&arg);
c.none("'.");
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::NoEquals,
info: vec![arg],
source: None,
}
}
pub(crate) fn invalid_value<G>(
bad_val: String,
good_vals: &[G],
arg: &Arg,
usage: String,
color: ColorChoice,
) -> Self
where
G: AsRef<str> + Display,
{
let mut c = Colorizer::new(true, color);
let suffix = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
let mut sorted: Vec<String> = good_vals
.iter()
.map(|v| v.to_string())
.map(|v| {
if v.contains(char::is_whitespace) {
format!("{:?}", v)
} else {
v
}
})
.collect();
sorted.sort();
start_error(&mut c, "");
c.warning(format!("{:?}", bad_val));
c.none(" isn't a valid value for '");
c.warning(arg.to_string());
c.none("'\n\t[possible values: ");
if let Some((last, elements)) = sorted.split_last() {
for v in elements {
c.good(v);
c.none(", ");
}
c.good(last);
}
c.none("]");
if let Some(val) = suffix {
c.none("\n\n\tDid you mean ");
c.good(format!("{:?}", val));
c.none("?");
}
put_usage(&mut c, usage);
try_help(&mut c);
let mut info = vec![arg.to_string(), bad_val];
info.extend(sorted);
Error {
message: c,
kind: ErrorKind::InvalidValue,
info: vec![],
source: None,
}
}
pub(crate) fn invalid_subcommand(
subcmd: String,
did_you_mean: String,
name: String,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, "The subcommand '");
c.warning(subcmd.clone());
c.none("' wasn't recognized\n\n\tDid you mean ");
c.good(did_you_mean);
c.none("");
c.none(format!(
"?\n\nIf you believe you received this message in error, try re-running with '{} ",
name
));
c.good("--");
c.none(format!(" {}'", subcmd));
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::InvalidSubcommand,
info: vec![subcmd],
source: None,
}
}
pub(crate) fn unrecognized_subcommand(
subcmd: String,
name: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, " The subcommand '");
c.warning(subcmd.clone());
c.none("' wasn't recognized\n\n");
c.warning("USAGE:");
c.none(format!("\n\t{} help <subcommands>...", name));
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::UnrecognizedSubcommand,
info: vec![subcmd],
source: None,
}
}
pub(crate) fn missing_required_argument(
required: Vec<String>,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
start_error(
&mut c,
"The following required arguments were not provided:",
);
let mut info = vec![];
for v in required {
c.none("\n ");
c.good(v.to_string());
info.push(v.to_string());
}
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::MissingRequiredArgument,
info,
source: None,
}
}
pub(crate) fn missing_subcommand(name: String, usage: String, color: ColorChoice) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, "'");
c.warning(name);
c.none("' requires a subcommand, but one was not provided");
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::MissingSubcommand,
info: vec![],
source: None,
}
}
pub(crate) fn invalid_utf8(usage: String, color: ColorChoice) -> Self {
let mut c = Colorizer::new(true, color);
start_error(
&mut c,
"Invalid UTF-8 was detected in one or more arguments",
);
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::InvalidUtf8,
info: vec![],
source: None,
}
}
pub(crate) fn too_many_occurrences(
arg: &Arg,
max_occurs: usize,
curr_occurs: usize,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
let verb = Error::singular_or_plural(curr_occurs);
start_error(&mut c, "The argument '");
c.warning(arg.to_string());
c.none("' allows at most ");
c.warning(max_occurs.to_string());
c.none(" occurrences, but ");
c.warning(curr_occurs.to_string());
c.none(format!(" {} provided", verb));
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::TooManyOccurrences,
info: vec![
arg.to_string(),
curr_occurs.to_string(),
max_occurs.to_string(),
],
source: None,
}
}
pub(crate) fn too_many_values(
val: String,
arg: &Arg,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, "The value '");
c.warning(val.clone());
c.none("' was provided to '");
c.warning(arg.to_string());
c.none("' but it wasn't expecting any more values");
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::TooManyValues,
info: vec![arg.to_string(), val],
source: None,
}
}
pub(crate) fn too_few_values(
arg: &Arg,
min_vals: usize,
curr_vals: usize,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
let verb = Error::singular_or_plural(curr_vals);
start_error(&mut c, "The argument '");
c.warning(arg.to_string());
c.none("' requires at least ");
c.warning(min_vals.to_string());
c.none(" values, but only ");
c.warning(curr_vals.to_string());
c.none(format!(" {} provided", verb));
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::TooFewValues,
info: vec![arg.to_string(), curr_vals.to_string(), min_vals.to_string()],
source: None,
}
}
pub(crate) fn value_validation(
arg: String,
val: String,
err: Box<dyn error::Error>,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, "Invalid value");
c.none(" for '");
c.warning(arg.clone());
c.none("'");
c.none(format!(": {}", err));
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::ValueValidation,
info: vec![arg, val, err.to_string()],
source: Some(err),
}
}
pub(crate) fn wrong_number_of_values(
arg: &Arg,
num_vals: usize,
curr_vals: usize,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
let verb = Error::singular_or_plural(curr_vals);
start_error(&mut c, "The argument '");
c.warning(arg.to_string());
c.none("' requires ");
c.warning(num_vals.to_string());
c.none(" values, but ");
c.warning(curr_vals.to_string());
c.none(format!(" {} provided", verb));
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::WrongNumberOfValues,
info: vec![arg.to_string(), curr_vals.to_string(), num_vals.to_string()],
source: None,
}
}
pub(crate) fn unexpected_multiple_usage(arg: &Arg, usage: String, color: ColorChoice) -> Self {
let mut c = Colorizer::new(true, color);
let arg = arg.to_string();
start_error(&mut c, "The argument '");
c.warning(arg.clone());
c.none("' was provided more than once, but cannot be used multiple times");
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::UnexpectedMultipleUsage,
info: vec![arg],
source: None,
}
}
pub(crate) fn unknown_argument(
arg: String,
did_you_mean: Option<(String, Option<String>)>,
usage: String,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, "Found argument '");
c.warning(arg.clone());
c.none("' which wasn't expected, or isn't valid in this context");
if let Some((flag, subcmd)) = did_you_mean {
let flag = format!("--{}", flag);
c.none("\n\n\tDid you mean ");
if let Some(subcmd) = subcmd {
c.none("to put '");
c.good(flag);
c.none("' after the subcommand '");
c.good(subcmd);
c.none("'?");
} else {
c.none("'");
c.good(flag);
c.none("'?");
}
}
// If the user wants to supply things like `--a-flag` or `-b` as a value,
// suggest `--` for disambiguation.
if arg.starts_with('-') {
c.none(format!(
"\n\n\tIf you tried to supply `{}` as a value rather than a flag, use `-- {}`",
arg, arg
));
}
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::UnknownArgument,
info: vec![arg],
source: None,
}
}
pub(crate) fn unnecessary_double_dash(arg: String, usage: String, color: ColorChoice) -> Self {
let mut c = Colorizer::new(true, color);
start_error(&mut c, "Found argument '");
c.warning(arg.clone());
c.none("' which wasn't expected, or isn't valid in this context");
c.none(format!(
"\n\n\tIf you tried to supply `{}` as a subcommand, remove the '--' before it.",
arg
));
put_usage(&mut c, usage);
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::UnknownArgument,
info: vec![arg],
source: None,
}
}
pub(crate) fn argument_not_found_auto(arg: String) -> Self {
let mut c = Colorizer::new(true, ColorChoice::Auto);
start_error(&mut c, "The argument '");
c.warning(arg.clone());
c.none("' wasn't found");
try_help(&mut c);
Error {
message: c,
kind: ErrorKind::ArgumentNotFound,
info: vec![arg],
source: None,
}
}
/// Create an error with a custom description.
///
/// This can be used in combination with `Error::exit` to exit your program
/// with a custom error message.
pub fn with_description(description: String, kind: ErrorKind) -> Self {
let mut c = Colorizer::new(true, ColorChoice::Auto);
start_error(&mut c, description);
Error {
message: c,
kind,
info: vec![],
source: None,
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::with_description(e.to_string(), ErrorKind::Io)
}
}
impl From<fmt::Error> for Error {
fn from(e: fmt::Error) -> Self {
Error::with_description(e.to_string(), ErrorKind::Format)
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.source.as_deref()
}
}