mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 15:27:16 +00:00
Merge pull request #4365 from epage/error
feat(error): Allow reproducing clap's errors
This commit is contained in:
commit
233a6be7dc
2 changed files with 104 additions and 23 deletions
|
@ -599,6 +599,54 @@ where
|
|||
}
|
||||
|
||||
/// Parse/validate argument values
|
||||
///
|
||||
/// As alternatives to implementing `TypedValueParser`,
|
||||
/// - Use `Fn(&str) -> Result<T, E>` 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<Self::Value, clap::Error> {
|
||||
/// 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;
|
||||
|
|
|
@ -98,6 +98,55 @@ impl<F: ErrorFormatter> Error<F> {
|
|||
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<F: ErrorFormatter> Error<F> {
|
|||
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<ContextValue> {
|
||||
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<F: ErrorFormatter> Error<F> {
|
|||
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<Message>) -> Self {
|
||||
self.inner.message = Some(message.into());
|
||||
self
|
||||
|
|
Loading…
Add table
Reference in a new issue