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/mod.rs

283 lines
6.2 KiB

//! Error handling stuffs (I think we'll go with this one in the end)
use std::{
error,
io,
cmp,
fmt,
str::{
FromStr,
},
borrow::Cow,
marker::{
Send, Sync
},
};
/// Result for all our operations, see `ErrorKind` below
///
/// This type can be used for when we want to propagate an error, but not pass it off to `eyre` reporter just yet, because we might still want to act on it and respond.
pub type Result<T> = std::result::Result<T, Error>;
mod context;
pub use context::*;
mod message;
use message::*;
mod wrap;
pub use wrap::*;
mod aggregate;
pub use aggregate::*;
impl Default for ErrorKind
{
#[inline]
fn default() -> Self
{
Self::None
}
}
impl Error
{
pub fn into_inner(self) -> ErrorKind
{
*self.internal
}
/// The kind of the error
pub fn kind(&self) -> &ErrorKind
{
&self.internal
}
/// The message for this error
pub fn message(&self) -> Option<Cow<'_, str>>
{
self.message.as_str()
}
/// The context from this error
pub fn context(&self) -> &Context
{
&self.context
}
/// Create a new error with a constant string
pub fn new_static(err: ErrorKind, ctx: Context, message: &'static str) -> Self
{
Self {
internal: box err,
context: ctx,
message: Message::from_str(message),
}
}
/// Create a new error with a string
pub fn new_string(err: ErrorKind, ctx: Context, message: String) -> Self
{
Self {
internal: box err,
context: ctx,
message: Message::from_string(message),
}
}
/// Create a new error with a message
pub fn new(err: ErrorKind, ctx: Context, message: impl fmt::Display + 'static + Send + Sync) -> Self
{
Self {
internal: box err,
context: ctx,
message: Message::from_other(message),
}
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if !self.context().is_empty() {
write!(f, "{}: ", self.context)?;
}
match &self.message {
Message::None => write!(f, "{}", self.internal),
message => write!(f, "{}", message.fmt())
}
}
}
impl error::Error for Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
match &self.message {
Message::None => std::iter::successors(self.internal.source(), |e| e.source()).take(1).next(),
_ => Some(&self.internal),
}
}
}
#[macro_export] macro_rules! context {
() => ($crate::error::Context::with_all(file!(), line!(), column!()));
($msg:expr) => ($crate::error::ContextMessage::new($crate::context!(), $msg));
(yield $fmt:literal $($tt:tt)*) => ($crate::context!(lazy_format::lazy_format!($fmt $($tt)*)));
($fmt:literal $($tt:tt)*) => ($crate::context!(format!($fmt $($tt)*)));
}
#[macro_export] macro_rules! error {
($kind:expr, $lit:literal) => ($crate::error::Error::new_static($kind, $crate::context!(), $lit));
($lit:literal) => ($crate::error!(Default::default(), $lit));
($kind:expr, $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::Error::new($kind, $crate::context!(), DisplayError($msg))
}
};
($msg:expr) => ($crate::error!(Default::default(), $msg));
(yield $kind:expr, $lit:literal $($tt:tt)*) => ($crate::error!($kind, lazy_format::lazy_format!($lit $($tt)*)));
($kind:expr, $lit:literal $($tt:tt)*) => ($crate::error!($kind, format!($lit $($tt)*)));
(yield $lit:literal $($tt:tt)*) => ($crate::error!(yield Default::default(), $lit $($tt)*));
($lit:literal $($tt:tt)*) => ($crate::error!(Default::default(), $lit $($tt)*));
}
#[derive(Debug)]
pub struct Error
{
internal: Box<ErrorKind>,
context: Context,
message: Message,
}
// Actual stuffs
#[non_exhaustive]
#[derive(Debug)]
pub enum ErrorKind
{
IO(io::Error),
Wrap(Error),
Any(Box<dyn error::Error +Send + Sync + 'static>),
None,
Unknown,
}
impl fmt::Display for ErrorKind
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::IO(io) => write!(f, "i/o error: {}", io),
Self::Wrap(wrap) => write!(f, "{}", wrap),
Self::None => Ok(()),
_ => write!(f, "unknown error"),
}
}
}
impl error::Error for ErrorKind
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(match &self {
Self::IO(io) => io,
Self::Wrap(wrap) => return wrap.source(),
_ => return None,
})
}
}
impl From<io::Error> for ErrorKind
{
fn from(from: io::Error) -> Self
{
Self::IO(from)
}
}
impl<T> From<T> for Error
where T: Into<ErrorKind>
{
fn from(from: T) -> Self
{
Self {
internal: box from.into(),
context: Default::default(),
message: Message::None,
}
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod test
{
use super::*;
use color_eyre::{
eyre::{
Result as ReportedResult,
},
Help,
SectionExt,
};
#[test]
fn test() -> ReportedResult<()>
{
color_eyre::install()?;
fn testt() -> Result<()> {
let err= error!(yield one().unwrap_err().into_inner(), "hello world {} {}", 1, 2);
Err(err)
.wrap_err_with(|| context!("with string"))
.wrap_err_with(|| context!(yield "with another string and some stuffs {}", 1))?;
Ok(())
}
fn works() -> Result<()>
{
Ok(())
}
fn one() -> Result<()>
{
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "end of records"))?;
Ok(())
}
fn two() -> Result<()>
{
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "nonexistent"))?;
Ok(())
}
fn three() -> Result<()>
{
Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, "piped"))
.wrap_err_with(|| context!("hello hello"))?;
Ok(())
}
Ok(one()
.aggregate_with(|_| two())
.aggregate_with(|_| works())
.aggregate_with(|_| three())?)
}
}