2023-06-14 18:22:10 +00:00
|
|
|
use std::fmt::Write;
|
|
|
|
use std::{error, fmt};
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
/// Error is a String which implements the Error trait. It is intended to be used in
|
|
|
|
/// situations where the caller is being given an error they can't really handle, except to pass it
|
|
|
|
/// along or log it.
|
|
|
|
///
|
|
|
|
/// The error is intended to also implement Send + Sync + Clone, such that it is easy to use in
|
|
|
|
/// async situations.
|
|
|
|
pub struct Error(String);
|
|
|
|
|
|
|
|
impl Error {
|
|
|
|
fn from_displays<D1, D2, D3>(prefix: Option<D1>, body: &D2, source: Option<D3>) -> Error
|
|
|
|
where
|
|
|
|
D1: fmt::Display,
|
|
|
|
D2: fmt::Display + ?Sized,
|
|
|
|
D3: fmt::Display,
|
|
|
|
{
|
|
|
|
let mut w = String::new();
|
|
|
|
|
|
|
|
if let Some(prefix) = prefix {
|
|
|
|
write!(w, "{prefix}").expect("error writing prefix");
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(w, "{body}").expect("error formatting body");
|
|
|
|
|
|
|
|
if let Some(source) = source {
|
|
|
|
write!(w, " [{source}]").expect("error formatting source");
|
|
|
|
}
|
|
|
|
|
|
|
|
Error(w.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_err<E, D>(prefix: Option<D>, e: E) -> Error
|
|
|
|
where
|
|
|
|
E: error::Error,
|
|
|
|
D: fmt::Display,
|
|
|
|
{
|
|
|
|
return Error::from_displays(prefix, &e, e.source());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from(s: &str) -> Error {
|
|
|
|
Error::from_displays(None::<String>, s, None::<String>)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "Unexpected error occurred: {}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl error::Error for Error {}
|
|
|
|
|
|
|
|
pub trait Mappable<T> {
|
|
|
|
/// or_unexpected returns an Err(Error) wrapping self's Err, or the Ok value of self.
|
|
|
|
fn or_unexpected(self) -> Result<T, Error>;
|
|
|
|
|
|
|
|
/// or_unexpected_while is like or_unexpected, but will prefix the error message. The prefix
|
|
|
|
/// should be worded as if it started with the word "while", e.g.: `opening file {path}`.
|
|
|
|
fn or_unexpected_while<D: fmt::Display>(self, prefix: D) -> Result<T, Error>;
|
|
|
|
|
|
|
|
/// map_unexpected_while is like or_unexpected_while, but uses a closure to produce the error
|
|
|
|
/// prefix.
|
|
|
|
fn map_unexpected_while<F, D>(self, f: F) -> Result<T, Error>
|
|
|
|
where
|
|
|
|
F: FnOnce() -> D,
|
|
|
|
D: fmt::Display;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn map_unexpected_maybe_while<T, E, F, D>(
|
|
|
|
res: Result<T, E>,
|
|
|
|
prefix_fn: Option<F>,
|
|
|
|
) -> Result<T, Error>
|
|
|
|
where
|
|
|
|
E: error::Error,
|
|
|
|
F: FnOnce() -> D,
|
|
|
|
D: std::fmt::Display,
|
|
|
|
{
|
|
|
|
match res {
|
|
|
|
Ok(res) => Ok(res),
|
|
|
|
Err(err) => Err(Error::from_err(prefix_fn.map(|prefix_fn| prefix_fn()), err)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T, E: error::Error> Mappable<T> for Result<T, E> {
|
|
|
|
fn or_unexpected(self) -> Result<T, Error> {
|
|
|
|
let no_fn = None::<Box<dyn FnOnce() -> Box<dyn fmt::Display>>>; // lol, good job rust
|
|
|
|
map_unexpected_maybe_while(self, no_fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn or_unexpected_while<D: fmt::Display>(self, prefix: D) -> Result<T, Error> {
|
|
|
|
map_unexpected_maybe_while(self, Some(|| prefix))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn map_unexpected_while<F, D>(self, f: F) -> Result<T, Error>
|
|
|
|
where
|
|
|
|
F: FnOnce() -> D,
|
|
|
|
D: fmt::Display,
|
|
|
|
{
|
|
|
|
map_unexpected_maybe_while(self, Some(f))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-21 11:15:42 +00:00
|
|
|
static OPTION_NONE_ERROR: &'static str = "expected Some but got None";
|
|
|
|
|
|
|
|
impl<T> Mappable<T> for Option<T> {
|
|
|
|
fn or_unexpected(self) -> Result<T, Error> {
|
|
|
|
self.ok_or(Error::from(OPTION_NONE_ERROR)).or_unexpected()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn or_unexpected_while<D: fmt::Display>(self, prefix: D) -> Result<T, Error> {
|
|
|
|
self.ok_or(Error::from(OPTION_NONE_ERROR))
|
|
|
|
.or_unexpected_while(prefix)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn map_unexpected_while<F, D>(self, f: F) -> Result<T, Error>
|
|
|
|
where
|
|
|
|
F: FnOnce() -> D,
|
|
|
|
D: fmt::Display,
|
|
|
|
{
|
|
|
|
self.ok_or(Error::from(OPTION_NONE_ERROR))
|
|
|
|
.map_unexpected_while(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-14 18:22:10 +00:00
|
|
|
pub trait Intoable {
|
|
|
|
fn into_unexpected(self) -> Error;
|
|
|
|
|
|
|
|
/// into_unexpected_while is like to_unexpected, but will prefix the Error error. The
|
|
|
|
/// prefix should be worded as if it started with the word "while", e.g.: `opening file
|
|
|
|
/// {path}`.
|
|
|
|
fn into_unexpected_while<D>(self, prefix: D) -> Error
|
|
|
|
where
|
|
|
|
D: fmt::Display;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E: error::Error> Intoable for E {
|
|
|
|
fn into_unexpected(self) -> Error {
|
|
|
|
Error::from_err(None::<String>, self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn into_unexpected_while<D>(self, prefix: D) -> Error
|
|
|
|
where
|
|
|
|
D: fmt::Display,
|
|
|
|
{
|
|
|
|
Error::from_err(Some(prefix), self)
|
|
|
|
}
|
|
|
|
}
|