bash_builtins/errors.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
//! Types to handle errors.
use crate::ffi;
use std::fmt;
use std::os::raw::c_int;
/// The error type for [`Builtin::call`].
///
/// Usually, you don't need to construct this type manually. Instead, use the
/// [`?` operator] for any [`Result`] in the body of the [`Builtin::call`]
/// method, and errors will be converted to this type.
///
/// However, if you want to return a specific exit code, use the
/// [`ExitCode`] variant.
///
/// [`?` operator]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
/// [`Builtin::call`]: crate::Builtin::call
/// [`ExitCode`]: Error::ExitCode
/// [`Result`]: std::result::Result
#[derive(Debug)]
pub enum Error {
/// Syntax error in usage.
Usage,
/// Exit with a specific code.
///
/// # Example
///
/// ```
/// use bash_builtins::{Args, Builtin, Error::ExitCode, Result};
///
/// # struct SomeName;
/// impl Builtin for SomeName {
/// fn call(&mut self, args: &mut Args) -> Result<()> {
/// // In this builtin, we return `127` if there are
/// // no arguments.
/// if args.is_empty() {
/// return Err(ExitCode(127));
/// }
///
/// // …
///
/// Ok(())
/// }
/// }
/// ```
ExitCode(c_int),
/// Wrapper for any error.
///
/// This variant is used when the builtin propagates any error inside
/// [`Builtin::call`].
///
/// # Example
///
/// ```
/// use std::fs;
/// use bash_builtins::{Args, Builtin, Error::ExitCode, Result};
///
/// # struct SomeName;
/// impl Builtin for SomeName {
/// fn call(&mut self, args: &mut Args) -> Result<()> {
/// # let _ = args;
///
/// // fs::read can return an `io::Error`, which is wrapped
/// // by `GenericError` and then used as the return value.
/// let _ = fs::read("/some/config/file")?;
///
/// // …
///
/// Ok(())
/// }
/// }
/// ```
///
/// [`Builtin::call`]: crate::Builtin::call
GenericError(Box<dyn std::error::Error>),
}
impl Error {
/// Returns `true` if this error should be printed to [`stderr`] when
/// it is the result of [`Builtin::call`].
///
/// [`Builtin::call`]: crate::Builtin::call
/// [`stderr`]: std::io::stderr
#[doc(hidden)]
pub fn print_on_return(&self) -> bool {
let ignore = matches!(self, Error::Usage | Error::ExitCode(_));
!ignore
}
/// Numeric exit code for the builtin invocation.
#[doc(hidden)]
pub fn exit_code(&self) -> c_int {
match self {
Error::Usage => ffi::exit::EX_USAGE,
Error::ExitCode(s) => *s,
_ => ffi::exit::EXECUTION_FAILURE,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Usage => fmt.write_str("usage error"),
Error::ExitCode(s) => write!(fmt, "exit code {}", s),
Error::GenericError(e) => e.fmt(fmt),
}
}
}
impl<E> From<E> for Error
where
E: std::error::Error + 'static,
{
fn from(error: E) -> Self {
Error::GenericError(Box::new(error))
}
}
/// A specialized [`Result`] type for this crate.
///
/// [`Result`]: std::result::Result
pub type Result<T> = std::result::Result<T, Error>;