diff --git a/src/error.rs b/src/error.rs index 1612ab9..ecb60b7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,133 +4,320 @@ use std::{ fmt, }; -pub trait Context +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)] +pub struct Context { - fn line(&self) -> Option; - fn column(&self) -> Option; - fn file(&self) -> Option<&str>; + file: Option<&'static str>, + line: Option, + column: Option, } -pub trait Reporter +impl Context { - type Error: error::Error + ?Sized; + pub fn with_all(file: &'static str, line: u32, column: u32) -> Self + { + Self { + file: Some(file), + line: Some(line), + column: Some(column) + } + } +} - #[inline] fn context(&self) -> Option<&dyn Context> +impl fmt::Display for Context +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - None + 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, "?") + } } - fn source(&self) -> &Self::Error; - - fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; - #[inline] fn long_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result +} + +#[macro_export] macro_rules! context { + () => ($crate::error::Context::with_all(file!(), line!(), column!())); +} + +#[derive(Debug)] +pub struct ContextError(E, Context) +where E: error::Error; + +impl ContextError +where E: error::Error +{ + pub fn with_context(from: E, ctx: Context) -> Self { - self.short_message(f) + Self(from, ctx) } } -pub struct ShortMessage(R) -where R: Reporter; +impl error::Error for ContextError +where E: error::Error + 'static +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> + { + Some(&self.0) + } +} -impl fmt::Display for ShortMessage -where R: Reporter +impl fmt::Display for ContextError +where E: error::Error { - #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.short_message(f) + write!(f, "{}: {}", self.1, self.0) } } -pub struct LongMessage(R) -where R: Reporter; +#[macro_export] macro_rules! error { + ($msg:expr) => { + { + pub struct DisplayError(D) where D: fmt::Display; + impl fmt::Debug for DisplayError + where D: fmt::Display + { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}", std::any::type_name::()) + } + } + impl fmt::Display for DisplayError + where D: fmt::Display + { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + self.0.fmt(f) + } + } + impl error::Error for DisplayError + where D: fmt::Display{} -impl fmt::Display for LongMessage -where R: Reporter + $crate::error::ContextError::with_context(DisplayError($msg), context!()) + } + }; + (&fmt:literal $($tt:tt)*) => ($crate::error!(format!($literal $($tt)*))); +} + +pub struct Wrap(E,D); + + +impl Wrap +where E: error::Error + 'static, + D: fmt::Display { - #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + pub fn long_message(&self) -> impl fmt::Display + '_ { - self.0.long_message(f) + + pub struct LongMessage<'a, E,D>(&'a Wrap); + + 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 Reporter for Box -where E: error::Error + ?Sized +impl fmt::Display for Wrap +where D: fmt::Display { - type Error = E; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}", self.1) + } +} - fn source(&self) -> &Self::Error +impl fmt::Debug for Wrap +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - &self + write!(f, "{}", std::any::type_name::()) } - - fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result +} + + +impl error::Error for Wrap +where E: error::Error + 'static, + D: fmt::Display +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { - write!(f, "{}", &self) + Some(&self.0) } } -impl<'a, R> Reporter for &'a R -where R: Reporter +pub trait WrapErrExt: Sized { - type Error = ::Error; - - #[inline] fn context(&self) -> Option<&dyn Context> + #[inline] fn wrap_err(self, val: D) -> Result> + where D: fmt::Display { - (*self).context() + self.wrap_err_with(|| val) } - fn source(&self) -> &Self::Error + fn wrap_err_with(self, func: F) -> Result> + where D: fmt::Display, + F: FnOnce() -> D; +} + +impl WrapErrExt for Result +{ + fn wrap_err_with(self, func: F) -> Result> + where D: fmt::Display, + F: FnOnce() -> D { - (*self).source() + 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` 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 Reporter for Box +where E: error::Error + ?Sized +{ + type Error = E; fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (*self).short_message(f) + write!(f, "{}", self) } - #[inline] fn long_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + + fn source(&self) -> &Self::Error { - (*self).long_message(f) + self } } -pub struct Wrap(E, M); +pub struct WrapReporter(Wrap); -impl Reporter for Wrap -where E: Reporter, -M: fmt::Display +impl Reporter for WrapReporter +where E: error::Error + 'static, + D: fmt::Display { - type Error = E::Error; + type Error = E; + + fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "{}", self.0) + } - #[inline] fn context(&self) -> Option<&dyn Context> + fn long_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.context() + write!(f, "{}", self.0.long_message()) } + fn source(&self) -> &Self::Error { - self.0.source() + &self.0.0 } - +} + +impl From> for WrapReporter +{ + fn from(from: Wrap) -> Self + { + Self(from) + } +} + +pub struct Report(Box) where E: ?Sized; + +impl From for Report +where E: error::Error +{ + fn from(from: E) -> Self + { + Self(Box::new(from)) + } +} +impl From> for Report +where E: error::Error + 'static + ?Sized +{ + fn from(from: Box) -> Self + { + Self(from) + } +} + +impl Reporter for Report +where E: error::Error + ?Sized +{ + type Error = E; + fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.1) + write!(f, "{}", self.0) } - #[inline] fn long_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + + fn source(&self) -> &Self::Error { - write!(f, "{}", self.1)?; - self.0.long_message(f) //ehh, also failed + &self.0 } } #[cfg(test)] -mod tests { +mod test +{ use super::*; - fn test() -> Box + + #[test] + fn test() { - Box::new(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "uhhhhh")) + 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 atest() + fn report() { - let bx = test(); - assert_eq!(format!("{}", LongMessage(&bx)), format!("{}", bx)); + fn one() -> Result<(), Report> + { + 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 + } + } }