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(prefix: Option, body: &D2, source: Option) -> 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(prefix: Option, 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::, s, None::) } } 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 { /// or_unexpected returns an Err(Error) wrapping self's Err, or the Ok value of self. fn or_unexpected(self) -> Result; /// 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(self, prefix: D) -> Result; /// map_unexpected_while is like or_unexpected_while, but uses a closure to produce the error /// prefix. fn map_unexpected_while(self, f: F) -> Result where F: FnOnce() -> D, D: fmt::Display; } fn map_unexpected_maybe_while( res: Result, prefix_fn: Option, ) -> Result 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 Mappable for Result { fn or_unexpected(self) -> Result { let no_fn = None:: Box>>; // lol, good job rust map_unexpected_maybe_while(self, no_fn) } fn or_unexpected_while(self, prefix: D) -> Result { map_unexpected_maybe_while(self, Some(|| prefix)) } fn map_unexpected_while(self, f: F) -> Result where F: FnOnce() -> D, D: fmt::Display, { map_unexpected_maybe_while(self, Some(f)) } } 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(self, prefix: D) -> Error where D: fmt::Display; } impl Intoable for E { fn into_unexpected(self) -> Error { Error::from_err(None::, self) } fn into_unexpected_while(self, prefix: D) -> Error where D: fmt::Display, { Error::from_err(Some(prefix), self) } }