From b31df047b882ebefe0d8139c0c075b4d914b19d9 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Mon, 5 Oct 2020 18:35:45 +0200 Subject: [PATCH] Attach validation error as Error::source --- clap_derive/examples/keyvalue.rs | 6 ++-- clap_derive/src/derives/into_app.rs | 1 - clap_derive/tests/custom-string-parsers.rs | 10 ++++++- src/build/arg/mod.rs | 18 +++++++----- src/parse/errors.rs | 32 ++++++++++++++++++++-- src/parse/matches/arg_matches.rs | 9 ++++-- src/parse/parser.rs | 3 ++ src/parse/validator.rs | 1 + 8 files changed, 63 insertions(+), 17 deletions(-) diff --git a/clap_derive/examples/keyvalue.rs b/clap_derive/examples/keyvalue.rs index 5bf952fb..c4724ca6 100644 --- a/clap_derive/examples/keyvalue.rs +++ b/clap_derive/examples/keyvalue.rs @@ -4,12 +4,12 @@ use clap::Clap; use std::error::Error; /// Parse a single key-value pair -fn parse_key_val(s: &str) -> Result<(T, U), Box> +fn parse_key_val(s: &str) -> Result<(T, U), Box> 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('=') diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs index 4570f749..01baca22 100644 --- a/clap_derive/src/derives/into_app.rs +++ b/clap_derive/src/derives/into_app.rs @@ -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()=> diff --git a/clap_derive/tests/custom-string-parsers.rs b/clap_derive/tests/custom-string-parsers.rs index d127cac9..8c6f8ad0 100644 --- a/clap_derive/tests/custom-string-parsers.rs +++ b/clap_derive/tests/custom-string-parsers.rs @@ -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 { diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs index a1cfd98e..1c555444 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg/mod.rs @@ -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> + Send + 'a; +type ValidatorOs<'a> = dyn FnMut(&OsStr) -> Result<(), Box> + 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(mut self, mut f: F) -> Self where F: FnMut(&str) -> Result + Send + 'help, - E: ToString, + E: Into>, { 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(mut self, mut f: F) -> Self + pub fn validator_os(mut self, mut f: F) -> Self where - F: FnMut(&OsStr) -> Result + Send + 'help, + F: FnMut(&OsStr) -> Result + Send + 'help, + E: Into>, { - 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 } diff --git a/src/parse/errors.rs b/src/parse/errors.rs index e44b6807..dfd01a7c 100644 --- a/src/parse/errors.rs +++ b/src/parse/errors.rs @@ -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, + pub(crate) source: Option>, } 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, 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 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 + } + } +} diff --git a/src/parse/matches/arg_matches.rs b/src/parse/matches/arg_matches.rs index 3fc3362b..c7d06e69 100644 --- a/src/parse/matches/arg_matches.rs +++ b/src/parse/matches/arg_matches.rs @@ -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, ) }) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 1df579fd..47bbc2e0 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -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, } } } diff --git a/src/parse/validator.rs b/src/parse/validator.rs index 8f63d525..2ba7c798 100644 --- a/src/parse/validator.rs +++ b/src/parse/validator.rs @@ -62,6 +62,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { message, kind: ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand, info: vec![], + source: None, }); } self.validate_conflicts(matcher)?;