use std::fmt;
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
#[derive(Debug)]
pub enum Error {
Msg(String),
WithContext { context: String, source: BoxError },
Boxed(BoxError),
}
impl Error {
pub fn msg<S: Into<String>>(s: S) -> Self {
Self::Msg(s.into())
}
pub fn new<E: Into<BoxError>>(e: E) -> Self {
Self::Boxed(e.into())
}
pub fn with_context<E: Into<BoxError>, S: Into<String>>(
e: E,
context: S,
) -> Self {
Self::WithContext {
context: context.into(),
source: e.into(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Msg(s) => f.write_str(s),
Self::WithContext { context, source } => {
if f.alternate() {
write!(f, "{context}: {source:#}")
} else {
write!(f, "{context}: {source}")
}
}
Self::Boxed(e) => {
if f.alternate() {
write!(f, "{e:#}")
} else {
write!(f, "{e}")
}
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::WithContext { source, .. } | Self::Boxed(source) => {
Some(&**source)
}
Self::Msg(_) => None,
}
}
}
impl From<String> for Error {
fn from(s: String) -> Self {
Self::Msg(s)
}
}
impl From<&str> for Error {
fn from(s: &str) -> Self {
Self::Msg(s.to_string())
}
}
macro_rules! impl_from {
($($ty:ty),* $(,)?) => {
$(
impl From<$ty> for Error {
fn from(e: $ty) -> Self {
Self::Boxed(Box::new(e))
}
}
)*
};
}
impl_from!(
bwx::error::Error,
std::io::Error,
serde_json::Error,
std::num::ParseIntError,
);
pub type Result<T> = std::result::Result<T, Error>;
pub trait ContextExt<T, E> {
fn context<S: Into<String>>(self, ctx: S) -> Result<T>;
fn with_context<S: Into<String>, F: FnOnce() -> S>(
self,
f: F,
) -> Result<T>;
}
impl<T, E: Into<BoxError>> ContextExt<T, E> for std::result::Result<T, E> {
fn context<S: Into<String>>(self, ctx: S) -> Result<T> {
self.map_err(|e| Error::with_context(e, ctx))
}
fn with_context<S: Into<String>, F: FnOnce() -> S>(
self,
f: F,
) -> Result<T> {
self.map_err(|e| Error::with_context(e, f()))
}
}
macro_rules! err {
($($arg:tt)*) => { $crate::bin_error::Error::msg(format!($($arg)*)) };
}
macro_rules! bail {
($($arg:tt)*) => { return Err($crate::bin_error::err!($($arg)*)) };
}
pub(crate) use bail;
pub(crate) use err;
impl<T> ContextExt<T, Error> for Option<T> {
fn context<S: Into<String>>(self, ctx: S) -> Result<T> {
self.ok_or_else(|| Error::msg(ctx))
}
fn with_context<S: Into<String>, F: FnOnce() -> S>(
self,
f: F,
) -> Result<T> {
self.ok_or_else(|| Error::msg(f()))
}
}