#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://wisagan.github.io/simple-error/simple_error/")]
#![deny(missing_docs)]
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SimpleError {
err: String,
}
impl SimpleError {
#[inline]
pub fn new<T: Into<String>>(t: T) -> SimpleError {
SimpleError{ err: t.into() }
}
#[inline]
pub fn from<T: std::error::Error>(t: T) -> SimpleError {
SimpleError{ err: format!("{t}") }
}
#[inline]
pub fn with<T: std::error::Error>(s: &str, t: T) -> SimpleError {
SimpleError{ err: format!("{s}, {t}") }
}
#[inline]
pub fn as_str(&self) -> &str {
&self.err
}
}
impl<'a> From<&'a str> for SimpleError {
#[inline]
fn from(s: &str) -> SimpleError {
SimpleError{ err: s.into() }
}
}
impl fmt::Display for SimpleError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.err.fmt(f)
}
}
impl std::error::Error for SimpleError {
}
pub type SimpleResult<T> = Result<T, SimpleError>;
#[macro_export]
macro_rules! try_with {
($expr: expr, $fmt:literal) => (match $expr {
Ok(val) => val,
Err(err) => {
return Err(::std::convert::From::from($crate::SimpleError::with(&format!($fmt), err)));
},
});
($expr: expr, $str: expr) => (match $expr {
Ok(val) => val,
Err(err) => {
return Err(::std::convert::From::from($crate::SimpleError::with($str.as_ref(), err)));
},
});
($expr: expr, $fmt:literal, $($arg:tt)+) => (match $expr {
Ok(val) => val,
Err(err) => {
return Err(::std::convert::From::from($crate::SimpleError::with(&format!($fmt, $($arg)+), err)));
},
});
}
#[macro_export]
macro_rules! require_with {
($expr: expr, $fmt:literal) => (match $expr {
Some(val) => val,
None => {
return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt))));
},
});
($expr: expr, $str: expr) => (match $expr {
Some(val) => val,
None => {
return Err(::std::convert::From::from($crate::SimpleError::new(::std::convert::AsRef::<str>::as_ref($str))));
},
});
($expr: expr, $fmt:literal, $($arg:tt)+) => (match $expr {
Some(val) => val,
None => {
return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt, $($arg)+))));
},
});
}
#[macro_export]
macro_rules! bail {
($fmt:literal) => {
return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt))));
};
($e:expr) => {
return Err(::std::convert::From::from($e));
};
($fmt:literal, $($arg:tt)+) => {
return Err(::std::convert::From::from($crate::SimpleError::new(format!($fmt, $($arg)+))));
};
}
#[macro_export]
macro_rules! simple_error {
($fmt:literal) => {
$crate::SimpleError::new(format!($fmt))
};
($e:expr) => {
$crate::SimpleError::new($e)
};
($fmt:literal, $($arg:tt)+) => {
$crate::SimpleError::new(format!($fmt, $($arg)+))
};
}
#[macro_export]
macro_rules! map_err_with {
($r: expr, $fmt:literal) => {
$r.map_err(|e| $crate::SimpleError::with(&format!($fmt), e))
};
($r: expr, $str: expr) => {
$r.map_err(|e| $crate::SimpleError::with($str.as_ref(), e))
};
($r: expr, $fmt:literal, $($arg:tt)+) => {
$r.map_err(|e| $crate::SimpleError::with(&format!($fmt, $($arg)+), e))
};
}
#[cfg(test)]
mod tests {
use super::SimpleError;
use std::error::Error;
use std::io;
pub struct ErrorSeed;
impl From<ErrorSeed> for SimpleError {
fn from(_: ErrorSeed) -> SimpleError {
SimpleError::new(".")
}
}
#[test]
fn new_from_string() {
let err = SimpleError::new(String::from("an error from String"));
assert_eq!("an error from String", format!("{err}"));
}
#[test]
fn new_from_str() {
let err = SimpleError::new("an error from str");
assert_eq!("an error from str", format!("{err}"));
}
#[test]
fn new_from_simple_error_macro() {
let err = SimpleError::new("an error from str");
assert_eq!(SimpleError::new("an error from str"), simple_error!("{err}"));
}
#[test]
fn map_error_with_macro() {
let err = crate::SimpleResult::<()>::Err(SimpleError::new("an error from str"));
let additional_reason = "error in current function";
assert_eq!(Err(SimpleError::new("error in current function, an error from str")), map_err_with!(err, "{additional_reason}"));
}
#[test]
fn from_io_error() {
let err = SimpleError::from(io::Error::new(io::ErrorKind::Other, "oh no"));
assert_eq!("oh no", format!("{err}"));
}
fn try_block(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
Ok(try_with!(result, s))
}
fn try_block_format(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
Ok(try_with!(result, "with {}", s))
}
fn try_block_format_with_capture(result: Result<(), SimpleError>, s: &str) -> Result<(), SimpleError> {
Ok(try_with!(result, "with {s}"))
}
#[test]
fn macro_try_with() {
assert_eq!(Ok(()), try_block(Ok(()), ""));
assert_eq!(Err(SimpleError::new("try block error, error foo")), try_block(Err(SimpleError::new("error foo")), "try block error"));
assert_eq!(Err(SimpleError::new("with try block error, error foo")), try_block_format(Err(SimpleError::new("error foo")), "try block error"));
assert_eq!(Err(SimpleError::new("with try block error, error foo")), try_block_format_with_capture(Err(SimpleError::new("error foo")), "try block error"));
}
fn require_block(option: Option<()>, s: &str) -> Result<(), SimpleError> {
Ok(require_with!(option, s))
}
fn require_block_str_as_ref(option: Option<()>, s: &String) -> Result<(), SimpleError> {
Ok(require_with!(option, s))
}
fn require_block_format(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
Ok(require_with!(maybe, "with {}", s))
}
fn require_block_format_with_capture(maybe: Option<()>, s: &str) -> Result<(), SimpleError> {
Ok(require_with!(maybe, "with {s}"))
}
#[test]
fn macro_require_with() {
assert_eq!(Ok(()), require_block(Some(()), ""));
assert_eq!(Err(SimpleError::new("require block error")), require_block(None, "require block error"));
assert_eq!(Err(SimpleError::new("require block error")), require_block_str_as_ref(None, &"require block error".to_owned()));
assert_eq!(Err(SimpleError::new("with require block error")), require_block_format(None, "require block error"));
assert_eq!(Err(SimpleError::new("with require block error")), require_block_format_with_capture(None, "require block error"));
}
fn bail_block_into(es: ErrorSeed) -> Result<(), SimpleError> {
bail!(es);
}
fn bail_block_str(s: &str) -> Result<(), SimpleError> {
bail!(s);
}
fn bail_block_format(s: &str) -> Result<(), SimpleError> {
bail!("reason: {}", s);
}
fn bail_block_format_with_capture(s: &str) -> Result<(), SimpleError> {
bail!("reason: {s}");
}
fn bail_block_format_to_box_error(s: &str) -> Result<(), Box<dyn Error>> {
bail!("reason: {}", s);
}
#[test]
fn macro_bail() {
assert_eq!(Err(SimpleError::new(".")), bail_block_into(ErrorSeed));
assert_eq!(Err(SimpleError::new("no reason")), bail_block_str("no reason"));
assert_eq!(Err(SimpleError::new("reason: plane crashed")), bail_block_format("plane crashed"));
assert!(bail_block_format_to_box_error("plane crashed").is_err());
}
#[test]
fn inline_format_arg_capture() {
assert_eq!(Err(SimpleError::new("reason: plane crashed")), bail_block_format_with_capture("plane crashed"));
}
}