Attach validation error as Error::source

This commit is contained in:
Wim Looman 2020-10-05 18:35:45 +02:00
parent 5a1a209965
commit b31df047b8
8 changed files with 63 additions and 17 deletions

View file

@ -4,12 +4,12 @@ use clap::Clap;
use std::error::Error;
/// Parse a single key-value pair
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error>>
fn parse_key_val<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
where
T: std::str::FromStr,
T::Err: Error + 'static,
T::Err: Error + Send + Sync + 'static,
U: std::str::FromStr,
U::Err: Error + 'static,
U::Err: Error + Send + Sync + 'static,
{
let pos = s
.find('=')

View file

@ -227,7 +227,6 @@ pub fn gen_app_augmentation(
.validator(|s| {
#func(s)
.map(|_: #convert_type| ())
.map_err(|e| e.to_string())
})
},
ParserKind::TryFromOsStr => quote_spanned! { func.span()=>

View file

@ -90,7 +90,15 @@ fn test_parse_hex() {
fn custom_parser_1(_: &str) -> &'static str {
"A"
}
fn custom_parser_2(_: &str) -> Result<&'static str, u32> {
#[derive(Debug)]
struct ErrCode(u32);
impl std::error::Error for ErrCode {}
impl std::fmt::Display for ErrCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
fn custom_parser_2(_: &str) -> Result<&'static str, ErrCode> {
Ok("B")
}
fn custom_parser_3(_: &OsStr) -> &'static str {

View file

@ -11,6 +11,7 @@ use std::{
borrow::Cow,
cmp::{Ord, Ordering},
env,
error::Error,
ffi::{OsStr, OsString},
fmt::{self, Display, Formatter},
str,
@ -38,8 +39,8 @@ use crate::{
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
type Validator<'a> = dyn FnMut(&str) -> Result<(), String> + Send + 'a;
type ValidatorOs<'a> = dyn FnMut(&OsStr) -> Result<(), String> + Send + 'a;
type Validator<'a> = dyn FnMut(&str) -> Result<(), Box<dyn Error + Send + Sync>> + Send + 'a;
type ValidatorOs<'a> = dyn FnMut(&OsStr) -> Result<(), Box<dyn Error + Send + Sync>> + Send + 'a;
/// The abstract representation of a command line argument. Used to set all the options and
/// relationships that define a valid argument for the program.
@ -1879,10 +1880,10 @@ impl<'help> Arg<'help> {
pub fn validator<F, O, E>(mut self, mut f: F) -> Self
where
F: FnMut(&str) -> Result<O, E> + Send + 'help,
E: ToString,
E: Into<Box<dyn Error + Send + Sync + 'static>>,
{
self.validator = Some(Arc::new(Mutex::new(move |s: &str| {
f(s).map(|_| ()).map_err(|e| e.to_string())
f(s).map(|_| ()).map_err(|e| e.into())
})));
self
}
@ -1917,11 +1918,14 @@ impl<'help> Arg<'help> {
/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
/// [`Err(String)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
/// [`Rc`]: https://doc.rust-lang.org/std/rc/struct.Rc.html
pub fn validator_os<F, O>(mut self, mut f: F) -> Self
pub fn validator_os<F, O, E>(mut self, mut f: F) -> Self
where
F: FnMut(&OsStr) -> Result<O, String> + Send + 'help,
F: FnMut(&OsStr) -> Result<O, E> + Send + 'help,
E: Into<Box<dyn Error + Send + Sync + 'static>>,
{
self.validator_os = Some(Arc::new(Mutex::new(move |s: &OsStr| f(s).map(|_| ()))));
self.validator_os = Some(Arc::new(Mutex::new(move |s: &OsStr| {
f(s).map(|_| ()).map_err(|e| e.into())
})));
self
}

View file

@ -385,6 +385,7 @@ pub struct Error {
/// 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 + Send + Sync>>,
}
impl Display for Error {
@ -478,6 +479,7 @@ impl Error {
message: c,
kind: ErrorKind::ArgumentConflict,
info,
source: None,
}
}
@ -495,6 +497,7 @@ impl Error {
message: c,
kind: ErrorKind::EmptyValue,
info: vec![arg],
source: None,
}
}
@ -557,6 +560,7 @@ impl Error {
message: c,
kind: ErrorKind::InvalidValue,
info: vec![],
source: None,
}
}
@ -587,6 +591,7 @@ impl Error {
message: c,
kind: ErrorKind::InvalidSubcommand,
info: vec![subcmd],
source: None,
}
}
@ -608,6 +613,7 @@ impl Error {
message: c,
kind: ErrorKind::UnrecognizedSubcommand,
info: vec![subcmd],
source: None,
}
}
@ -637,6 +643,7 @@ impl Error {
message: c,
kind: ErrorKind::MissingRequiredArgument,
info,
source: None,
}
}
@ -653,6 +660,7 @@ impl Error {
message: c,
kind: ErrorKind::MissingSubcommand,
info: vec![],
source: None,
}
}
@ -670,6 +678,7 @@ impl Error {
message: c,
kind: ErrorKind::InvalidUtf8,
info: vec![],
source: None,
}
}
@ -693,6 +702,7 @@ impl Error {
message: c,
kind: ErrorKind::TooManyValues,
info: vec![arg.to_string(), val],
source: None,
}
}
@ -720,13 +730,14 @@ impl 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: String,
err: Box<dyn error::Error + Send + Sync>,
color: ColorChoice,
) -> Self {
let mut c = Colorizer::new(true, color);
@ -743,7 +754,8 @@ impl Error {
Error {
message: c,
kind: ErrorKind::ValueValidation,
info: vec![arg, val, err],
info: vec![arg, val, err.to_string()],
source: Some(err),
}
}
@ -771,6 +783,7 @@ impl Error {
message: c,
kind: ErrorKind::WrongNumberOfValues,
info: vec![arg.to_string(), curr_vals.to_string(), num_vals.to_string()],
source: None,
}
}
@ -788,6 +801,7 @@ impl Error {
message: c,
kind: ErrorKind::UnexpectedMultipleUsage,
info: vec![arg],
source: None,
}
}
@ -836,6 +850,7 @@ impl Error {
message: c,
kind: ErrorKind::UnknownArgument,
info: vec![arg],
source: None,
}
}
@ -857,6 +872,7 @@ impl Error {
message: c,
kind: ErrorKind::UnknownArgument,
info: vec![arg],
source: None,
}
}
@ -872,6 +888,7 @@ impl Error {
message: c,
kind: ErrorKind::ArgumentNotFound,
info: vec![arg],
source: None,
}
}
@ -888,6 +905,7 @@ impl Error {
message: c,
kind,
info: vec![],
source: None,
}
}
}
@ -904,4 +922,12 @@ impl From<fmt::Error> for Error {
}
}
impl error::Error for Error {}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
if let Some(source) = self.source.as_deref() {
Some(source)
} else {
None
}
}
}

View file

@ -351,7 +351,12 @@ impl ArgMatches {
v, name, e
);
Error::value_validation(name.to_string(), v.to_string(), message, ColorChoice::Auto)
Error::value_validation(
name.to_string(),
v.to_string(),
message.into(),
ColorChoice::Auto,
)
})
} else {
Err(Error::argument_not_found_auto(name.to_string()))
@ -439,7 +444,7 @@ impl ArgMatches {
Error::value_validation(
name.to_string(),
v.to_string(),
message,
message.into(),
ColorChoice::Auto,
)
})

View file

@ -752,6 +752,7 @@ impl<'help, 'app> Parser<'help, 'app> {
message,
kind: ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
info: vec![],
source: None,
});
}
}
@ -1747,6 +1748,7 @@ impl<'help, 'app> Parser<'help, 'app> {
message: c,
kind: ErrorKind::DisplayHelp,
info: vec![],
source: None,
},
}
}
@ -1761,6 +1763,7 @@ impl<'help, 'app> Parser<'help, 'app> {
message: c,
kind: ErrorKind::DisplayVersion,
info: vec![],
source: None,
}
}
}

View file

@ -62,6 +62,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
message,
kind: ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
info: vec![],
source: None,
});
}
self.validate_conflicts(matcher)?;