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.
283 lines
6.2 KiB
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())?)
|
|
}
|
|
}
|