diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs index 9944f758bce..6aece7a78a3 100644 --- a/src/builder/value_parser.rs +++ b/src/builder/value_parser.rs @@ -599,6 +599,54 @@ where } /// Parse/validate argument values +/// +/// As alternatives to implementing `TypedValueParser`, +/// - Use `Fn(&str) -> Result` which implements `TypedValueParser` +/// - [`TypedValueParser::map`] to adapt an existing `TypedValueParser` +/// +/// See `ValueParserFactory` for register `TypedValueParser::Value` with +/// [`value_parser!`][crate::value_parser]. +/// +/// # Example +/// +#[cfg_attr(not(feature = "error-context"), doc = " ```ignore")] +#[cfg_attr(feature = "error-context", doc = " ```")] +/// # use clap::error::ErrorKind; +/// # use clap::error::ContextKind; +/// # use clap::error::ContextValue; +/// #[derive(Clone)] +/// struct Custom(u32); +/// +/// #[derive(Clone)] +/// struct CustomValueParser; +/// +/// impl clap::builder::TypedValueParser for CustomValueParser { +/// type Value = Custom; +/// +/// fn parse_ref( +/// &self, +/// cmd: &clap::Command, +/// arg: Option<&clap::Arg>, +/// value: &std::ffi::OsStr, +/// ) -> Result { +/// let inner = clap::value_parser!(u32); +/// let val = inner.parse_ref(cmd, arg, value)?; +/// +/// const INVALID_VALUE: u32 = 10; +/// if val == INVALID_VALUE { +/// let mut err = clap::Error::new(ErrorKind::ValueValidation) +/// .with_cmd(cmd); +/// if let Some(arg) = arg { +/// err.insert(ContextKind::InvalidArg, ContextValue::String(arg.to_string())); +/// } +/// err.insert(ContextKind::InvalidValue, ContextValue::String(INVALID_VALUE.to_string())); +/// return Err(err); +/// } +/// +/// Ok(Custom(val)) +/// } +/// } +/// ``` pub trait TypedValueParser: Clone + Send + Sync + 'static { /// Argument's value type type Value: Send + Sync + Clone; diff --git a/src/error/mod.rs b/src/error/mod.rs index 12622924767..932e9065073 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -98,6 +98,55 @@ impl Error { self.with_cmd(cmd) } + /// Create an error with a pre-defined message + /// + /// See also + /// - [`Error::insert`] + /// - [`Error::with_cmd`] + /// + /// # Example + /// + #[cfg_attr(not(feature = "error-context"), doc = " ```ignore")] + #[cfg_attr(feature = "error-context", doc = " ```")] + /// # use clap::error::ErrorKind; + /// # use clap::error::ContextKind; + /// # use clap::error::ContextValue; + /// + /// let cmd = clap::Command::new("prog"); + /// + /// let mut err = clap::Error::new(ErrorKind::ValueValidation) + /// .with_cmd(&cmd); + /// err.insert(ContextKind::InvalidArg, ContextValue::String("--foo".to_owned())); + /// err.insert(ContextKind::InvalidValue, ContextValue::String("bar".to_owned())); + /// + /// err.print(); + /// ``` + pub fn new(kind: ErrorKind) -> Self { + Self { + inner: Box::new(ErrorInner { + kind, + #[cfg(feature = "error-context")] + context: FlatMap::new(), + message: None, + source: None, + help_flag: None, + color_when: ColorChoice::Never, + color_help_when: ColorChoice::Never, + backtrace: Backtrace::new(), + }), + phantom: Default::default(), + } + } + + /// Apply [`Command`]'s formatting to the error + /// + /// Generally, this is used with [`Error::new`] + pub fn with_cmd(self, cmd: &Command) -> Self { + self.set_color(cmd.get_color()) + .set_colored_help(cmd.color_help()) + .set_help_flag(format::get_help_flag(cmd)) + } + /// Apply an alternative formatter to the error /// /// # Example @@ -138,6 +187,13 @@ impl Error { self.inner.context.get(&kind) } + /// Insert a piece of context + #[inline(never)] + #[cfg(feature = "error-context")] + pub fn insert(&mut self, kind: ContextKind, value: ContextValue) -> Option { + self.inner.context.insert(kind, value) + } + /// Should the message be written to `stdout` or not? #[inline] pub fn use_stderr(&self) -> bool { @@ -216,34 +272,11 @@ impl Error { self.formatted().into_owned() } - fn new(kind: ErrorKind) -> Self { - Self { - inner: Box::new(ErrorInner { - kind, - #[cfg(feature = "error-context")] - context: FlatMap::new(), - message: None, - source: None, - help_flag: None, - color_when: ColorChoice::Never, - color_help_when: ColorChoice::Never, - backtrace: Backtrace::new(), - }), - phantom: Default::default(), - } - } - #[inline(never)] fn for_app(kind: ErrorKind, cmd: &Command, styled: StyledStr) -> Self { Self::new(kind).set_message(styled).with_cmd(cmd) } - pub(crate) fn with_cmd(self, cmd: &Command) -> Self { - self.set_color(cmd.get_color()) - .set_colored_help(cmd.color_help()) - .set_help_flag(format::get_help_flag(cmd)) - } - pub(crate) fn set_message(mut self, message: impl Into) -> Self { self.inner.message = Some(message.into()); self