use std::error::Error;
use std::fmt;
use std::io;
use std::sync::Arc;
use super::{Location, Trackable};
pub type BoxError = Box<dyn Error + Send + Sync>;
pub type BoxErrorKind = Box<dyn ErrorKind + Send + Sync>;
pub type History = ::History<Location>;
#[derive(Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Failed;
impl ErrorKind for Failed {
fn description(&self) -> &str {
"Failed"
}
}
#[derive(Debug, Clone, TrackableError)]
#[trackable(error_kind = "Failed")]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Failure(TrackableError<Failed>);
impl Failure {
pub fn from_error<E>(error: E) -> Self
where
E: Into<BoxError>,
{
Failed.cause(error).into()
}
}
#[derive(Debug, Clone, TrackableError)]
#[trackable(error_kind = "io::ErrorKind")]
pub struct IoError(TrackableError<io::ErrorKind>);
impl From<IoError> for io::Error {
fn from(f: IoError) -> Self {
io::Error::new(*f.kind(), f)
}
}
impl From<io::Error> for IoError {
fn from(f: io::Error) -> Self {
f.kind().cause(f).into()
}
}
impl From<Failure> for IoError {
fn from(f: Failure) -> Self {
io::ErrorKind::Other.takes_over(f).into()
}
}
impl ErrorKind for io::ErrorKind {
fn description(&self) -> &str {
"I/O Error"
}
}
pub type TestError = TopLevelError;
pub type MainError = TopLevelError;
pub struct TopLevelError(Box<dyn Error>);
impl<E: Error + Trackable + 'static> From<E> for TopLevelError {
fn from(e: E) -> Self {
TopLevelError(Box::new(e))
}
}
impl fmt::Debug for TopLevelError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for TopLevelError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Error for TopLevelError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&*self.0)
}
}
pub trait ErrorKind: fmt::Debug {
fn description(&self) -> &str {
"An error"
}
fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl ErrorKind for String {
fn description(&self) -> &str {
self
}
}
pub trait ErrorKindExt: ErrorKind + Sized {
#[inline]
fn error(self) -> TrackableError<Self> {
self.into()
}
#[inline]
fn cause<E>(self, cause: E) -> TrackableError<Self>
where
E: Into<BoxError>,
{
TrackableError::new(self, cause.into())
}
fn takes_over<F, K>(self, from: F) -> TrackableError<Self>
where
F: Into<TrackableError<K>>,
K: ErrorKind + Send + Sync + 'static,
{
let from = from.into();
TrackableError {
kind: self,
cause: from.cause,
history: from.history,
}
}
}
impl<T: ErrorKind> ErrorKindExt for T {}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct TrackableError<K> {
kind: K,
cause: Option<Cause>,
history: History,
}
impl<K: ErrorKind> TrackableError<K> {
pub fn new<E>(kind: K, cause: E) -> Self
where
E: Into<BoxError>,
{
TrackableError {
kind,
cause: Some(Cause(Arc::new(cause.into()))),
history: History::new(),
}
}
fn from_kind(kind: K) -> Self {
TrackableError {
kind,
cause: None,
history: History::new(),
}
}
#[inline]
pub fn kind(&self) -> &K {
&self.kind
}
#[inline]
pub fn concrete_cause<T>(&self) -> Option<&T>
where
T: Error + 'static,
{
self.cause.as_ref().and_then(|c| c.0.downcast_ref())
}
}
impl<K: ErrorKind> From<K> for TrackableError<K> {
#[inline]
fn from(kind: K) -> Self {
Self::from_kind(kind)
}
}
impl<K: ErrorKind + Default> Default for TrackableError<K> {
#[inline]
fn default() -> Self {
Self::from_kind(K::default())
}
}
impl<K: ErrorKind> fmt::Display for TrackableError<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.kind.display(f)?;
if let Some(ref e) = self.cause {
write!(f, " (cause; {})", e.0)?;
}
write!(f, "\n{}", self.history)?;
Ok(())
}
}
impl<K: ErrorKind> Error for TrackableError<K> {
fn description(&self) -> &str {
self.kind.description()
}
fn cause(&self) -> Option<&dyn Error> {
self.cause.as_ref().map::<&dyn Error, _>(|e| &**e.0)
}
}
impl<K> Trackable for TrackableError<K> {
type Event = Location;
#[inline]
fn history(&self) -> Option<&History> {
Some(&self.history)
}
#[inline]
fn history_mut(&mut self) -> Option<&mut History> {
Some(&mut self.history)
}
}
#[derive(Debug, Clone)]
struct Cause(Arc<BoxError>);
#[cfg(feature = "serialize")]
mod impl_serde {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::sync::Arc;
use super::Cause;
impl Serialize for Cause {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> Deserialize<'de> for Cause {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Cause(Arc::new(s.into())))
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std;
#[test]
fn it_works() {
#[derive(Debug, TrackableError)]
#[trackable(error_kind = "MyErrorKind")]
struct MyError(TrackableError<MyErrorKind>);
impl From<std::io::Error> for MyError {
fn from(f: std::io::Error) -> Self {
MyErrorKind::Critical.cause(f).into()
}
}
#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
enum MyErrorKind {
Critical,
NonCritical,
}
impl ErrorKind for MyErrorKind {}
let error: MyError = MyErrorKind::Critical.cause("something wrong").into();
let error = track!(error);
let error = track!(error, "I passed here");
assert_eq!(
format!("\nError: {}", error).replace('\\', "/"),
r#"
Error: Critical (cause; something wrong)
HISTORY:
[0] at src/error.rs:508
[1] at src/error.rs:509 -- I passed here
"#
);
let result = (|| -> Result<_, MyError> {
let f =
track!(std::fs::File::open("/path/to/non_existent_file").map_err(MyError::from,))?;
Ok(f)
})();
let error = result.err().unwrap();
let cause = error.concrete_cause::<std::io::Error>().unwrap();
assert_eq!(cause.kind(), std::io::ErrorKind::NotFound);
}
}