use alloc::{
boxed::Box,
string::{String, ToString as _},
};
use core::fmt;
use std::io;
pub(crate) type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(Debug)]
pub struct Error(ErrorKind);
#[derive(Debug)]
pub(crate) enum ErrorKind {
Io(io::Error),
Json(serde_json::Error),
Other(Box<str>),
WithContext(Box<str>, Option<Box<dyn std::error::Error + Send + Sync>>),
}
impl Error {
pub(crate) fn new(e: impl Into<ErrorKind>) -> Self {
Self(e.into())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
ErrorKind::Io(e) => fmt::Display::fmt(e, f),
ErrorKind::Json(e) => fmt::Display::fmt(e, f),
ErrorKind::Other(e) | ErrorKind::WithContext(e, ..) => fmt::Display::fmt(e, f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.0 {
ErrorKind::Io(e) => e.source(),
ErrorKind::Json(e) => e.source(),
ErrorKind::Other(_) => None,
ErrorKind::WithContext(_, e) => Some(&**e.as_ref()?),
}
}
}
impl From<io::Error> for ErrorKind {
fn from(e: io::Error) -> Self {
Self::Io(e)
}
}
impl From<String> for ErrorKind {
fn from(e: String) -> Self {
Self::Other(e.into_boxed_str())
}
}
impl From<serde_json::Error> for ErrorKind {
fn from(e: serde_json::Error) -> Self {
Self::Json(e)
}
}
pub(crate) trait Context<T, E> {
fn with_context<C, F>(self, context: F) -> Result<T, Error>
where
C: fmt::Display,
F: FnOnce() -> C;
}
impl<T, E> Context<T, E> for Result<T, E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn with_context<C, F>(self, context: F) -> Result<T, Error>
where
C: fmt::Display,
F: FnOnce() -> C,
{
match self {
Ok(ok) => Ok(ok),
Err(e) => Err(Error(ErrorKind::WithContext(
context().to_string().into_boxed_str(),
Some(Box::new(e)),
))),
}
}
}
impl<T> Context<T, core::convert::Infallible> for Option<T> {
fn with_context<C, F>(self, context: F) -> Result<T, Error>
where
C: fmt::Display,
F: FnOnce() -> C,
{
match self {
Some(ok) => Ok(ok),
None => {
Err(Error(ErrorKind::WithContext(context().to_string().into_boxed_str(), None)))
}
}
}
}