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; use std::error::Error;
/// Parse a single key-value pair /// 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 where
T: std::str::FromStr, T: std::str::FromStr,
T::Err: Error + 'static, T::Err: Error + Send + Sync + 'static,
U: std::str::FromStr, U: std::str::FromStr,
U::Err: Error + 'static, U::Err: Error + Send + Sync + 'static,
{ {
let pos = s let pos = s
.find('=') .find('=')

View file

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

View file

@ -90,7 +90,15 @@ fn test_parse_hex() {
fn custom_parser_1(_: &str) -> &'static str { fn custom_parser_1(_: &str) -> &'static str {
"A" "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") Ok("B")
} }
fn custom_parser_3(_: &OsStr) -> &'static str { fn custom_parser_3(_: &OsStr) -> &'static str {

View file

@ -11,6 +11,7 @@ use std::{
borrow::Cow, borrow::Cow,
cmp::{Ord, Ordering}, cmp::{Ord, Ordering},
env, env,
error::Error,
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
str, str,
@ -38,8 +39,8 @@ use crate::{
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
use yaml_rust::Yaml; use yaml_rust::Yaml;
type Validator<'a> = dyn FnMut(&str) -> 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<(), String> + 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 /// The abstract representation of a command line argument. Used to set all the options and
/// relationships that define a valid argument for the program. /// 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 pub fn validator<F, O, E>(mut self, mut f: F) -> Self
where where
F: FnMut(&str) -> Result<O, E> + Send + 'help, 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| { 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 self
} }
@ -1917,11 +1918,14 @@ impl<'help> Arg<'help> {
/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html /// [`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 /// [`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 /// [`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 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 self
} }

View file

@ -385,6 +385,7 @@ pub struct Error {
/// Additional information depending on the error kind, like values and argument names. /// Additional information depending on the error kind, like values and argument names.
/// Useful when you want to render an error of your own. /// Useful when you want to render an error of your own.
pub info: Vec<String>, pub info: Vec<String>,
pub(crate) source: Option<Box<dyn error::Error + Send + Sync>>,
} }
impl Display for Error { impl Display for Error {
@ -478,6 +479,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::ArgumentConflict, kind: ErrorKind::ArgumentConflict,
info, info,
source: None,
} }
} }
@ -495,6 +497,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::EmptyValue, kind: ErrorKind::EmptyValue,
info: vec![arg], info: vec![arg],
source: None,
} }
} }
@ -557,6 +560,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::InvalidValue, kind: ErrorKind::InvalidValue,
info: vec![], info: vec![],
source: None,
} }
} }
@ -587,6 +591,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::InvalidSubcommand, kind: ErrorKind::InvalidSubcommand,
info: vec![subcmd], info: vec![subcmd],
source: None,
} }
} }
@ -608,6 +613,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::UnrecognizedSubcommand, kind: ErrorKind::UnrecognizedSubcommand,
info: vec![subcmd], info: vec![subcmd],
source: None,
} }
} }
@ -637,6 +643,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::MissingRequiredArgument, kind: ErrorKind::MissingRequiredArgument,
info, info,
source: None,
} }
} }
@ -653,6 +660,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::MissingSubcommand, kind: ErrorKind::MissingSubcommand,
info: vec![], info: vec![],
source: None,
} }
} }
@ -670,6 +678,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::InvalidUtf8, kind: ErrorKind::InvalidUtf8,
info: vec![], info: vec![],
source: None,
} }
} }
@ -693,6 +702,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::TooManyValues, kind: ErrorKind::TooManyValues,
info: vec![arg.to_string(), val], info: vec![arg.to_string(), val],
source: None,
} }
} }
@ -720,13 +730,14 @@ impl Error {
message: c, message: c,
kind: ErrorKind::TooFewValues, kind: ErrorKind::TooFewValues,
info: vec![arg.to_string(), curr_vals.to_string(), min_vals.to_string()], info: vec![arg.to_string(), curr_vals.to_string(), min_vals.to_string()],
source: None,
} }
} }
pub(crate) fn value_validation( pub(crate) fn value_validation(
arg: String, arg: String,
val: String, val: String,
err: String, err: Box<dyn error::Error + Send + Sync>,
color: ColorChoice, color: ColorChoice,
) -> Self { ) -> Self {
let mut c = Colorizer::new(true, color); let mut c = Colorizer::new(true, color);
@ -743,7 +754,8 @@ impl Error {
Error { Error {
message: c, message: c,
kind: ErrorKind::ValueValidation, 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, message: c,
kind: ErrorKind::WrongNumberOfValues, kind: ErrorKind::WrongNumberOfValues,
info: vec![arg.to_string(), curr_vals.to_string(), num_vals.to_string()], info: vec![arg.to_string(), curr_vals.to_string(), num_vals.to_string()],
source: None,
} }
} }
@ -788,6 +801,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::UnexpectedMultipleUsage, kind: ErrorKind::UnexpectedMultipleUsage,
info: vec![arg], info: vec![arg],
source: None,
} }
} }
@ -836,6 +850,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::UnknownArgument, kind: ErrorKind::UnknownArgument,
info: vec![arg], info: vec![arg],
source: None,
} }
} }
@ -857,6 +872,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::UnknownArgument, kind: ErrorKind::UnknownArgument,
info: vec![arg], info: vec![arg],
source: None,
} }
} }
@ -872,6 +888,7 @@ impl Error {
message: c, message: c,
kind: ErrorKind::ArgumentNotFound, kind: ErrorKind::ArgumentNotFound,
info: vec![arg], info: vec![arg],
source: None,
} }
} }
@ -888,6 +905,7 @@ impl Error {
message: c, message: c,
kind, kind,
info: vec![], 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 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 { } else {
Err(Error::argument_not_found_auto(name.to_string())) Err(Error::argument_not_found_auto(name.to_string()))
@ -439,7 +444,7 @@ impl ArgMatches {
Error::value_validation( Error::value_validation(
name.to_string(), name.to_string(),
v.to_string(), v.to_string(),
message, message.into(),
ColorChoice::Auto, ColorChoice::Auto,
) )
}) })

View file

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

View file

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