Skip to content

Commit

Permalink
Merge pull request #1388 from scampi/issue-861
Browse files Browse the repository at this point in the history
feat(Error): add a cause field to the Error struct
  • Loading branch information
kbknapp authored Nov 3, 2019
2 parents 85f820f + 4d69942 commit 2a50c2e
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 42 deletions.
168 changes: 134 additions & 34 deletions src/parse/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,9 @@ pub enum ErrorKind {
/// Command Line Argument Parser Error
#[derive(Debug)]
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,
/// The type of error
pub kind: ErrorKind,
Expand All @@ -383,6 +385,15 @@ pub struct 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
pub fn use_stderr(&self) -> bool {
match self.kind {
Expand Down Expand Up @@ -421,28 +432,45 @@ impl Error {
use_stderr: true,
when: color,
});
let (plain_cause, colored_cause) = match other {
Some(name) => {
let n = name.into();
v.push(n.clone());
(
format!("The argument '{}' cannot be used with '{}'", group.name, n),
format!(
"The argument '{}' cannot be used with '{}'",
group.name,
c.warning(n)
),
)
}
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!(
"{} The argument '{}' cannot be used with {}\n\n\
{}\n\n\
For more information try {}",
"{} {}\n\n{}\n\nFor more information try {}",
c.error("error:"),
c.warning(group.name),
match other {
Some(name) => {
let n = name.into();
v.push(n.clone());
c.warning(format!("'{}'", n))
}
None => c.none("one or more of the other specified arguments".to_owned()),
},
colored_cause,
usage,
c.good("--help")
),
kind: ErrorKind::ArgumentConflict,
info: Some(v),
}
}

#[doc(hidden)]
pub fn argument_conflict<O, U>(arg: &Arg, other: Option<O>, usage: U, color: ColorWhen) -> Self
where
Expand All @@ -454,21 +482,37 @@ impl Error {
use_stderr: true,
when: color,
});
let (plain_cause, colored_cause) = match other {
Some(name) => {
let n = name.into();
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))
),
)
}
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!(
"{} The argument '{}' cannot be used with {}\n\n\
{}\n\n\
For more information try {}",
"{} {}\n\n{}\n\nFor more information try {}",
c.error("error:"),
c.warning(&*arg.to_string()),
match other {
Some(name) => {
let n = name.into();
v.push(n.clone());
c.warning(format!("'{}'", n))
}
None => c.none("one or more of the other specified arguments".to_owned()),
},
colored_cause,
usage,
c.good("--help")
),
Expand All @@ -487,6 +531,10 @@ impl Error {
when: color,
});
Error {
cause: format!(
"The argument '{}' requires a value but none was supplied",
arg
),
message: format!(
"{} The argument '{}' requires a value but none was supplied\
\n\n\
Expand Down Expand Up @@ -529,6 +577,13 @@ impl Error {
sorted.sort();
let valid_values = sorted.join(", ");
Error {
cause: format!(
"'{}' isn't a valid value for '{}'\n\t\
[possible values: {}]",
bad_val.as_ref(),
arg,
valid_values
),
message: format!(
"{} '{}' isn't a valid value for '{}'\n\t\
[possible values: {}]\n\
Expand Down Expand Up @@ -568,6 +623,7 @@ impl Error {
when: color,
});
Error {
cause: format!("The subcommand '{}' wasn't recognized", s),
message: format!(
"{} The subcommand '{}' wasn't recognized\n\t\
Did you mean '{}'?\n\n\
Expand Down Expand Up @@ -601,6 +657,7 @@ impl Error {
when: color,
});
Error {
cause: format!("The subcommand '{}' wasn't recognized", s),
message: format!(
"{} The subcommand '{}' wasn't recognized\n\n\
{}\n\t\
Expand Down Expand Up @@ -628,6 +685,10 @@ impl Error {
when: color,
});
Error {
cause: format!(
"The following required arguments were not provided:{}",
required
),
message: format!(
"{} The following required arguments were not provided:{}\n\n\
{}\n\n\
Expand All @@ -653,6 +714,7 @@ impl Error {
when: color,
});
Error {
cause: format!("'{}' requires a subcommand, but one was not provided", name),
message: format!(
"{} '{}' requires a subcommand, but one was not provided\n\n\
{}\n\n\
Expand All @@ -677,6 +739,7 @@ impl Error {
when: color,
});
Error {
cause: "Invalid UTF-8 was detected in one or more arguments".to_string(),
message: format!(
"{} Invalid UTF-8 was detected in one or more arguments\n\n\
{}\n\n\
Expand All @@ -702,6 +765,10 @@ impl Error {
when: color,
});
Error {
cause: format!(
"The value '{}' was provided to '{}', but it wasn't expecting any more values",
v, arg
),
message: format!(
"{} The value '{}' was provided to '{}', but it wasn't expecting \
any more values\n\n\
Expand Down Expand Up @@ -733,17 +800,22 @@ impl Error {
use_stderr: true,
when: color,
});
let verb = Error::singular_or_plural(curr_vals);
Error {
cause: format!(
"The argument '{}' requires at least {} values, but only {} {} provided",
arg, min_vals, curr_vals, verb
),
message: format!(
"{} The argument '{}' requires at least {} values, but only {} w{} \
"{} The argument '{}' requires at least {} values, but only {} {} \
provided\n\n\
{}\n\n\
For more information try {}",
c.error("error:"),
c.warning(arg.to_string()),
c.warning(min_vals.to_string()),
c.warning(curr_vals.to_string()),
if curr_vals > 1 { "ere" } else { "as" },
verb,
usage,
c.good("--help")
),
Expand All @@ -759,6 +831,15 @@ impl Error {
when: color,
});
Error {
cause: format!(
"Invalid value{}: {}",
if let Some(a) = arg {
format!(" for '{}'", a)
} else {
String::new()
},
err
),
message: format!(
"{} Invalid value{}: {}",
c.error("error:"),
Expand All @@ -781,33 +862,36 @@ impl Error {
}

#[doc(hidden)]
pub fn wrong_number_of_values<S, U>(
pub fn wrong_number_of_values<U>(
arg: &Arg,
num_vals: u64,
curr_vals: usize,
suffix: S,
usage: U,
color: ColorWhen,
) -> Self
where
S: Display,
U: Display,
{
let c = Colorizer::new(&ColorizerOption {
use_stderr: true,
when: color,
});
let verb = Error::singular_or_plural(curr_vals);
Error {
cause: format!(
"The argument '{}' requires {} values, but {} {} provided",
arg, num_vals, curr_vals, verb
),
message: format!(
"{} The argument '{}' requires {} values, but {} w{} \
"{} The argument '{}' requires {} values, but {} {}
provided\n\n\
{}\n\n\
For more information try {}",
c.error("error:"),
c.warning(arg.to_string()),
c.warning(num_vals.to_string()),
c.warning(curr_vals.to_string()),
suffix,
verb,
usage,
c.good("--help")
),
Expand All @@ -826,6 +910,10 @@ impl Error {
when: color,
});
Error {
cause: format!(
"The argument '{}' was provided more than once, but cannot be used multiple times",
arg
),
message: format!(
"{} The argument '{}' was provided more than once, but cannot \
be used multiple times\n\n\
Expand All @@ -842,7 +930,12 @@ impl Error {
}

#[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
A: Into<String>,
U: Display,
Expand All @@ -857,10 +950,14 @@ impl Error {

let did_you_mean_message = match did_you_mean {
Some(s) => format!("{}\n", s),
_ => "\n".to_owned()
_ => "\n".to_owned(),
};

Error {
cause: format!(
"Found argument '{}' which wasn't expected, or isn't valid in this context{}",
a, did_you_mean_message
),
message: format!(
"{} Found argument '{}' which wasn't expected, or isn't valid in \
this context{}\
Expand All @@ -886,6 +983,7 @@ impl Error {
when: color,
});
Error {
cause: e.description().to_string(),
message: format!("{} {}", c.error("error:"), e.description()),
kind: ErrorKind::Io,
info: None,
Expand All @@ -903,6 +1001,7 @@ impl Error {
when: ColorWhen::Auto,
});
Error {
cause: format!("The argument '{}' wasn't found", a),
message: format!(
"{} The argument '{}' wasn't found",
c.error("error:"),
Expand All @@ -923,6 +1022,7 @@ impl Error {
when: ColorWhen::Auto,
});
Error {
cause: description.to_string(),
message: format!("{} {}", c.error("error:"), description),
kind,
info: None,
Expand Down
3 changes: 3 additions & 0 deletions src/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,7 @@ where
let mut out = vec![];
self.write_help_err(&mut out)?;
return Err(ClapError {
cause: String::new(),
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
Expand Down Expand Up @@ -1534,6 +1535,7 @@ where
match Help::new(&mut buf, self, use_long, false).write_help() {
Err(e) => e,
_ => ClapError {
cause: String::new(),
message: String::from_utf8(buf).unwrap_or_default(),
kind: ErrorKind::HelpDisplayed,
info: None,
Expand All @@ -1548,6 +1550,7 @@ where
match self.print_version(&mut buf_w, use_long) {
Err(e) => e,
_ => ClapError {
cause: String::new(),
message: String::new(),
kind: ErrorKind::VersionDisplayed,
info: None,
Expand Down
Loading

0 comments on commit 2a50c2e

Please sign in to comment.