You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
videl/src/error.rs

324 lines
6.2 KiB

//! Error handling stuffs
use std::{
error,
fmt,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
pub struct Context
{
file: Option<&'static str>,
line: Option<u32>,
column: Option<u32>,
}
impl Context
{
pub fn with_all(file: &'static str, line: u32, column: u32) -> Self
{
Self {
file: Some(file),
line: Some(line),
column: Some(column)
}
}
}
impl fmt::Display for Context
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if let Some(file) = self.file {
write!(f, "{} ", file)
} else {
write!(f, "? ")
}?;
if let Some(line) = self.line {
write!(f, "{}:", line)
} else {
write!(f, "?:")
}?;
if let Some(column) = self.column {
write!(f, "{}", column)
} else {
write!(f, "?")
}
}
}
#[macro_export] macro_rules! context {
() => ($crate::error::Context::with_all(file!(), line!(), column!()));
}
#[derive(Debug)]
pub struct ContextError<E>(E, Context)
where E: error::Error;
impl<E> ContextError<E>
where E: error::Error
{
pub fn with_context(from: E, ctx: Context) -> Self
{
Self(from, ctx)
}
}
impl<E> error::Error for ContextError<E>
where E: error::Error + 'static
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(&self.0)
}
}
impl<E> fmt::Display for ContextError<E>
where E: error::Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}: {}", self.1, self.0)
}
}
#[macro_export] macro_rules! error {
($msg:expr) => {
{
pub struct DisplayError<D>(D) where D: fmt::Display;
impl<D> fmt::Debug for DisplayError<D>
where D: fmt::Display
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", std::any::type_name::<Self>())
}
}
impl<D> fmt::Display for DisplayError<D>
where D: fmt::Display
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.0.fmt(f)
}
}
impl<D> error::Error for DisplayError<D>
where D: fmt::Display{}
$crate::error::ContextError::with_context(DisplayError($msg), context!())
}
};
(&fmt:literal $($tt:tt)*) => ($crate::error!(format!($literal $($tt)*)));
}
pub struct Wrap<E,D>(E,D);
impl<E,D> Wrap<E,D>
where E: error::Error + 'static,
D: fmt::Display
{
pub fn long_message(&self) -> impl fmt::Display + '_
{
pub struct LongMessage<'a, E,D>(&'a Wrap<E,D>);
impl<'a, E,D> fmt::Display for LongMessage<'a,E,D>
where E: error::Error + 'static,
D: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use error::Error;
for (num, next) in (1..).zip(std::iter::successors(self.0.source(), |e| e.source()))
{
writeln!(f, " [{}] -> {}", num, next)?;
}
Ok(())
}
}
LongMessage(self)
}
}
impl<E,D> fmt::Display for Wrap<E,D>
where D: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.1)
}
}
impl<E,D> fmt::Debug for Wrap<E,D>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", std::any::type_name::<Self>())
}
}
impl<E,D> error::Error for Wrap<E,D>
where E: error::Error + 'static,
D: fmt::Display
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(&self.0)
}
}
pub trait WrapErrExt<T,E>: Sized
{
#[inline] fn wrap_err<F,D>(self, val: D) -> Result<T, Wrap<E, D>>
where D: fmt::Display
{
self.wrap_err_with(|| val)
}
fn wrap_err_with<F,D>(self, func: F) -> Result<T, Wrap<E, D>>
where D: fmt::Display,
F: FnOnce() -> D;
}
impl<T,E> WrapErrExt<T,E> for Result<T,E>
{
fn wrap_err_with<F,D>(self, func: F) -> Result<T, Wrap<E, D>>
where D: fmt::Display,
F: FnOnce() -> D
{
self.map_err(|e| Wrap(e, func()))
}
}
// We can now define the reporter trait, like last time, and then out own reporter type for `Wrap<E,D>` that prints its long message properly
pub trait Reporter
{
type Error: error::Error + ?Sized;
#[inline] fn long_message(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result
{
Ok(())
}
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
fn source(&self) -> &Self::Error;
}
impl<E> Reporter for Box<E>
where E: error::Error + ?Sized
{
type Error = E;
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self)
}
fn source(&self) -> &Self::Error
{
self
}
}
pub struct WrapReporter<E,D>(Wrap<E,D>);
impl<E,D> Reporter for WrapReporter<E,D>
where E: error::Error + 'static,
D: fmt::Display
{
type Error = E;
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0)
}
fn long_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0.long_message())
}
fn source(&self) -> &Self::Error
{
&self.0.0
}
}
impl<E,D> From<Wrap<E,D>> for WrapReporter<E,D>
{
fn from(from: Wrap<E,D>) -> Self
{
Self(from)
}
}
pub struct Report<E>(Box<E>) where E: ?Sized;
impl<E> From<E> for Report<E>
where E: error::Error
{
fn from(from: E) -> Self
{
Self(Box::new(from))
}
}
impl<E> From<Box<E>> for Report<E>
where E: error::Error + 'static + ?Sized
{
fn from(from: Box<E>) -> Self
{
Self(from)
}
}
impl<E> Reporter for Report<E>
where E: error::Error + ?Sized
{
type Error = E;
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0)
}
fn source(&self) -> &Self::Error
{
&self.0
}
}
#[cfg(test)]
mod test
{
use super::*;
#[test]
fn test()
{
let err: Result<(), _> = Err(error!("hello world"))
.wrap_err_with(|| error!("thing failed"))
.wrap_err_with(|| error!("top level failed again"));
if let Err(err) = err
{
panic!("{}\n\n{}", err, err.long_message());
}
}
#[test]
fn report()
{
fn one() -> Result<(), Report<dyn error::Error>>
{
let err: Result<(), _> = Err(error!("hello world"))
.wrap_err_with(|| error!("thing failed"))
.wrap_err_with(|| error!("top level failed again"));
Ok(err?) //failed again. just fucking use eyre
}
}
}