mirror of
https://github.com/clap-rs/clap
synced 2024-12-15 23:32:32 +00:00
Merge pull request #1388 from scampi/issue-861
feat(Error): add a cause field to the Error struct
This commit is contained in:
commit
2a50c2ef80
3 changed files with 140 additions and 44 deletions
|
@ -374,7 +374,9 @@ pub enum ErrorKind {
|
||||||
/// Command Line Argument Parser Error
|
/// Command Line Argument Parser Error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
/// Formatted error message
|
/// The cause of the error
|
||||||
|
pub cause: String,
|
||||||
|
/// Formatted error message, enhancing the cause message with extra information
|
||||||
pub message: String,
|
pub message: String,
|
||||||
/// The type of error
|
/// The type of error
|
||||||
pub kind: ErrorKind,
|
pub kind: ErrorKind,
|
||||||
|
@ -383,6 +385,15 @@ pub struct Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
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
|
/// Should the message be written to `stdout` or not
|
||||||
pub fn use_stderr(&self) -> bool {
|
pub fn use_stderr(&self) -> bool {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
|
@ -421,21 +432,37 @@ impl Error {
|
||||||
use_stderr: true,
|
use_stderr: true,
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
let (plain_cause, colored_cause) = match other {
|
||||||
message: format!(
|
|
||||||
"{} The argument '{}' cannot be used with {}\n\n\
|
|
||||||
{}\n\n\
|
|
||||||
For more information try {}",
|
|
||||||
c.error("error:"),
|
|
||||||
c.warning(group.name),
|
|
||||||
match other {
|
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let n = name.into();
|
let n = name.into();
|
||||||
v.push(n.clone());
|
v.push(n.clone());
|
||||||
c.warning(format!("'{}'", n))
|
(
|
||||||
|
format!("The argument '{}' cannot be used with '{}'", group.name, n),
|
||||||
|
format!(
|
||||||
|
"The argument '{}' cannot be used with '{}'",
|
||||||
|
group.name,
|
||||||
|
c.warning(n)
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => c.none("one or more of the other specified arguments".to_owned()),
|
None => {
|
||||||
},
|
let n = "one or more of the other specified arguments";
|
||||||
|
(
|
||||||
|
format!("The argument '{}' cannot be used with {}", group.name, n),
|
||||||
|
format!(
|
||||||
|
"The argument '{}' cannot be used with {}",
|
||||||
|
group.name,
|
||||||
|
c.none(n)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Error {
|
||||||
|
cause: plain_cause,
|
||||||
|
message: format!(
|
||||||
|
"{} {}\n\n{}\n\nFor more information try {}",
|
||||||
|
c.error("error:"),
|
||||||
|
colored_cause,
|
||||||
usage,
|
usage,
|
||||||
c.good("--help")
|
c.good("--help")
|
||||||
),
|
),
|
||||||
|
@ -443,6 +470,7 @@ impl Error {
|
||||||
info: Some(v),
|
info: Some(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn argument_conflict<O, U>(arg: &Arg, other: Option<O>, usage: U, color: ColorWhen) -> Self
|
pub fn argument_conflict<O, U>(arg: &Arg, other: Option<O>, usage: U, color: ColorWhen) -> Self
|
||||||
where
|
where
|
||||||
|
@ -454,21 +482,37 @@ impl Error {
|
||||||
use_stderr: true,
|
use_stderr: true,
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
let (plain_cause, colored_cause) = match other {
|
||||||
message: format!(
|
|
||||||
"{} The argument '{}' cannot be used with {}\n\n\
|
|
||||||
{}\n\n\
|
|
||||||
For more information try {}",
|
|
||||||
c.error("error:"),
|
|
||||||
c.warning(&*arg.to_string()),
|
|
||||||
match other {
|
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let n = name.into();
|
let n = name.into();
|
||||||
v.push(n.clone());
|
v.push(n.clone());
|
||||||
|
(
|
||||||
|
format!("The argument '{}' cannot be used with '{}'", arg, n),
|
||||||
|
format!(
|
||||||
|
"The argument '{}' cannot be used with {}",
|
||||||
|
c.warning(arg.to_string()),
|
||||||
c.warning(format!("'{}'", n))
|
c.warning(format!("'{}'", n))
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => c.none("one or more of the other specified arguments".to_owned()),
|
None => {
|
||||||
},
|
let n = "one or more of the other specified arguments";
|
||||||
|
(
|
||||||
|
format!("The argument '{}' cannot be used with {}", arg, n),
|
||||||
|
format!(
|
||||||
|
"The argument '{}' cannot be used with {}",
|
||||||
|
c.warning(arg.to_string()),
|
||||||
|
c.none(n)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Error {
|
||||||
|
cause: plain_cause,
|
||||||
|
message: format!(
|
||||||
|
"{} {}\n\n{}\n\nFor more information try {}",
|
||||||
|
c.error("error:"),
|
||||||
|
colored_cause,
|
||||||
usage,
|
usage,
|
||||||
c.good("--help")
|
c.good("--help")
|
||||||
),
|
),
|
||||||
|
@ -487,6 +531,10 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"The argument '{}' requires a value but none was supplied",
|
||||||
|
arg
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The argument '{}' requires a value but none was supplied\
|
"{} The argument '{}' requires a value but none was supplied\
|
||||||
\n\n\
|
\n\n\
|
||||||
|
@ -529,6 +577,13 @@ impl Error {
|
||||||
sorted.sort();
|
sorted.sort();
|
||||||
let valid_values = sorted.join(", ");
|
let valid_values = sorted.join(", ");
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"'{}' isn't a valid value for '{}'\n\t\
|
||||||
|
[possible values: {}]",
|
||||||
|
bad_val.as_ref(),
|
||||||
|
arg,
|
||||||
|
valid_values
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} '{}' isn't a valid value for '{}'\n\t\
|
"{} '{}' isn't a valid value for '{}'\n\t\
|
||||||
[possible values: {}]\n\
|
[possible values: {}]\n\
|
||||||
|
@ -568,6 +623,7 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!("The subcommand '{}' wasn't recognized", s),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The subcommand '{}' wasn't recognized\n\t\
|
"{} The subcommand '{}' wasn't recognized\n\t\
|
||||||
Did you mean '{}'?\n\n\
|
Did you mean '{}'?\n\n\
|
||||||
|
@ -601,6 +657,7 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!("The subcommand '{}' wasn't recognized", s),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The subcommand '{}' wasn't recognized\n\n\
|
"{} The subcommand '{}' wasn't recognized\n\n\
|
||||||
{}\n\t\
|
{}\n\t\
|
||||||
|
@ -628,6 +685,10 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"The following required arguments were not provided:{}",
|
||||||
|
required
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The following required arguments were not provided:{}\n\n\
|
"{} The following required arguments were not provided:{}\n\n\
|
||||||
{}\n\n\
|
{}\n\n\
|
||||||
|
@ -653,6 +714,7 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!("'{}' requires a subcommand, but one was not provided", name),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} '{}' requires a subcommand, but one was not provided\n\n\
|
"{} '{}' requires a subcommand, but one was not provided\n\n\
|
||||||
{}\n\n\
|
{}\n\n\
|
||||||
|
@ -677,6 +739,7 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: "Invalid UTF-8 was detected in one or more arguments".to_string(),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} Invalid UTF-8 was detected in one or more arguments\n\n\
|
"{} Invalid UTF-8 was detected in one or more arguments\n\n\
|
||||||
{}\n\n\
|
{}\n\n\
|
||||||
|
@ -702,6 +765,10 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"The value '{}' was provided to '{}', but it wasn't expecting any more values",
|
||||||
|
v, arg
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The value '{}' was provided to '{}', but it wasn't expecting \
|
"{} The value '{}' was provided to '{}', but it wasn't expecting \
|
||||||
any more values\n\n\
|
any more values\n\n\
|
||||||
|
@ -733,9 +800,14 @@ impl Error {
|
||||||
use_stderr: true,
|
use_stderr: true,
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
|
let verb = Error::singular_or_plural(curr_vals);
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"The argument '{}' requires at least {} values, but only {} {} provided",
|
||||||
|
arg, min_vals, curr_vals, verb
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The argument '{}' requires at least {} values, but only {} w{} \
|
"{} The argument '{}' requires at least {} values, but only {} {} \
|
||||||
provided\n\n\
|
provided\n\n\
|
||||||
{}\n\n\
|
{}\n\n\
|
||||||
For more information try {}",
|
For more information try {}",
|
||||||
|
@ -743,7 +815,7 @@ impl Error {
|
||||||
c.warning(arg.to_string()),
|
c.warning(arg.to_string()),
|
||||||
c.warning(min_vals.to_string()),
|
c.warning(min_vals.to_string()),
|
||||||
c.warning(curr_vals.to_string()),
|
c.warning(curr_vals.to_string()),
|
||||||
if curr_vals > 1 { "ere" } else { "as" },
|
verb,
|
||||||
usage,
|
usage,
|
||||||
c.good("--help")
|
c.good("--help")
|
||||||
),
|
),
|
||||||
|
@ -759,6 +831,15 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"Invalid value{}: {}",
|
||||||
|
if let Some(a) = arg {
|
||||||
|
format!(" for '{}'", a)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
},
|
||||||
|
err
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} Invalid value{}: {}",
|
"{} Invalid value{}: {}",
|
||||||
c.error("error:"),
|
c.error("error:"),
|
||||||
|
@ -781,25 +862,28 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn wrong_number_of_values<S, U>(
|
pub fn wrong_number_of_values<U>(
|
||||||
arg: &Arg,
|
arg: &Arg,
|
||||||
num_vals: u64,
|
num_vals: u64,
|
||||||
curr_vals: usize,
|
curr_vals: usize,
|
||||||
suffix: S,
|
|
||||||
usage: U,
|
usage: U,
|
||||||
color: ColorWhen,
|
color: ColorWhen,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
S: Display,
|
|
||||||
U: Display,
|
U: Display,
|
||||||
{
|
{
|
||||||
let c = Colorizer::new(&ColorizerOption {
|
let c = Colorizer::new(&ColorizerOption {
|
||||||
use_stderr: true,
|
use_stderr: true,
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
|
let verb = Error::singular_or_plural(curr_vals);
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"The argument '{}' requires {} values, but {} {} provided",
|
||||||
|
arg, num_vals, curr_vals, verb
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The argument '{}' requires {} values, but {} w{} \
|
"{} The argument '{}' requires {} values, but {} {}
|
||||||
provided\n\n\
|
provided\n\n\
|
||||||
{}\n\n\
|
{}\n\n\
|
||||||
For more information try {}",
|
For more information try {}",
|
||||||
|
@ -807,7 +891,7 @@ impl Error {
|
||||||
c.warning(arg.to_string()),
|
c.warning(arg.to_string()),
|
||||||
c.warning(num_vals.to_string()),
|
c.warning(num_vals.to_string()),
|
||||||
c.warning(curr_vals.to_string()),
|
c.warning(curr_vals.to_string()),
|
||||||
suffix,
|
verb,
|
||||||
usage,
|
usage,
|
||||||
c.good("--help")
|
c.good("--help")
|
||||||
),
|
),
|
||||||
|
@ -826,6 +910,10 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"The argument '{}' was provided more than once, but cannot be used multiple times",
|
||||||
|
arg
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The argument '{}' was provided more than once, but cannot \
|
"{} The argument '{}' was provided more than once, but cannot \
|
||||||
be used multiple times\n\n\
|
be used multiple times\n\n\
|
||||||
|
@ -842,7 +930,12 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn unknown_argument<A, U>(arg: A, did_you_mean: Option<String>, usage: U, color: ColorWhen) -> Self
|
pub fn unknown_argument<A, U>(
|
||||||
|
arg: A,
|
||||||
|
did_you_mean: Option<String>,
|
||||||
|
usage: U,
|
||||||
|
color: ColorWhen,
|
||||||
|
) -> Self
|
||||||
where
|
where
|
||||||
A: Into<String>,
|
A: Into<String>,
|
||||||
U: Display,
|
U: Display,
|
||||||
|
@ -857,10 +950,14 @@ impl Error {
|
||||||
|
|
||||||
let did_you_mean_message = match did_you_mean {
|
let did_you_mean_message = match did_you_mean {
|
||||||
Some(s) => format!("{}\n", s),
|
Some(s) => format!("{}\n", s),
|
||||||
_ => "\n".to_owned()
|
_ => "\n".to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!(
|
||||||
|
"Found argument '{}' which wasn't expected, or isn't valid in this context{}",
|
||||||
|
a, did_you_mean_message
|
||||||
|
),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} Found argument '{}' which wasn't expected, or isn't valid in \
|
"{} Found argument '{}' which wasn't expected, or isn't valid in \
|
||||||
this context{}\
|
this context{}\
|
||||||
|
@ -886,6 +983,7 @@ impl Error {
|
||||||
when: color,
|
when: color,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: e.description().to_string(),
|
||||||
message: format!("{} {}", c.error("error:"), e.description()),
|
message: format!("{} {}", c.error("error:"), e.description()),
|
||||||
kind: ErrorKind::Io,
|
kind: ErrorKind::Io,
|
||||||
info: None,
|
info: None,
|
||||||
|
@ -903,6 +1001,7 @@ impl Error {
|
||||||
when: ColorWhen::Auto,
|
when: ColorWhen::Auto,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: format!("The argument '{}' wasn't found", a),
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} The argument '{}' wasn't found",
|
"{} The argument '{}' wasn't found",
|
||||||
c.error("error:"),
|
c.error("error:"),
|
||||||
|
@ -923,6 +1022,7 @@ impl Error {
|
||||||
when: ColorWhen::Auto,
|
when: ColorWhen::Auto,
|
||||||
});
|
});
|
||||||
Error {
|
Error {
|
||||||
|
cause: description.to_string(),
|
||||||
message: format!("{} {}", c.error("error:"), description),
|
message: format!("{} {}", c.error("error:"), description),
|
||||||
kind,
|
kind,
|
||||||
info: None,
|
info: None,
|
||||||
|
|
|
@ -700,6 +700,7 @@ where
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
self.write_help_err(&mut out)?;
|
self.write_help_err(&mut out)?;
|
||||||
return Err(ClapError {
|
return Err(ClapError {
|
||||||
|
cause: String::new(),
|
||||||
message: String::from_utf8_lossy(&*out).into_owned(),
|
message: String::from_utf8_lossy(&*out).into_owned(),
|
||||||
kind: ErrorKind::MissingArgumentOrSubcommand,
|
kind: ErrorKind::MissingArgumentOrSubcommand,
|
||||||
info: None,
|
info: None,
|
||||||
|
@ -1534,6 +1535,7 @@ where
|
||||||
match Help::new(&mut buf, self, use_long, false).write_help() {
|
match Help::new(&mut buf, self, use_long, false).write_help() {
|
||||||
Err(e) => e,
|
Err(e) => e,
|
||||||
_ => ClapError {
|
_ => ClapError {
|
||||||
|
cause: String::new(),
|
||||||
message: String::from_utf8(buf).unwrap_or_default(),
|
message: String::from_utf8(buf).unwrap_or_default(),
|
||||||
kind: ErrorKind::HelpDisplayed,
|
kind: ErrorKind::HelpDisplayed,
|
||||||
info: None,
|
info: None,
|
||||||
|
@ -1548,6 +1550,7 @@ where
|
||||||
match self.print_version(&mut buf_w, use_long) {
|
match self.print_version(&mut buf_w, use_long) {
|
||||||
Err(e) => e,
|
Err(e) => e,
|
||||||
_ => ClapError {
|
_ => ClapError {
|
||||||
|
cause: String::new(),
|
||||||
message: String::new(),
|
message: String::new(),
|
||||||
kind: ErrorKind::VersionDisplayed,
|
kind: ErrorKind::VersionDisplayed,
|
||||||
info: None,
|
info: None,
|
||||||
|
|
|
@ -67,6 +67,7 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
self.p.write_help_err(&mut out)?;
|
self.p.write_help_err(&mut out)?;
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
|
cause: String::new(),
|
||||||
message: String::from_utf8_lossy(&*out).into_owned(),
|
message: String::from_utf8_lossy(&*out).into_owned(),
|
||||||
kind: ErrorKind::MissingArgumentOrSubcommand,
|
kind: ErrorKind::MissingArgumentOrSubcommand,
|
||||||
info: None,
|
info: None,
|
||||||
|
@ -398,14 +399,6 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
|
||||||
} else {
|
} else {
|
||||||
ma.vals.len()
|
ma.vals.len()
|
||||||
},
|
},
|
||||||
if ma.vals.len() == 1
|
|
||||||
|| (a.is_set(ArgSettings::MultipleValues)
|
|
||||||
&& (ma.vals.len() % num as usize) == 1)
|
|
||||||
{
|
|
||||||
"as"
|
|
||||||
} else {
|
|
||||||
"ere"
|
|
||||||
},
|
|
||||||
&*Usage::new(self.p).create_usage_with_title(&[]),
|
&*Usage::new(self.p).create_usage_with_title(&[]),
|
||||||
self.p.app.color(),
|
self.p.app.color(),
|
||||||
));
|
));
|
||||||
|
|
Loading…
Reference in a new issue