trackable/
error.rs

1//! Functionalities for implementing trackable errors and operating on those.
2//!
3//! You can easily define your own trackable error types as follows:
4//!
5//! ```
6//! #[macro_use]
7//! extern crate trackable;
8//! use trackable::error::{TrackableError, ErrorKind, ErrorKindExt};
9//!
10//! #[derive(Debug, TrackableError)]
11//! #[trackable(error_kind = "MyErrorKind")]
12//! struct MyError(TrackableError<MyErrorKind>);
13//! impl From<std::io::Error> for MyError {
14//!     fn from(f: std::io::Error) -> Self {
15//!         // Any I/O errors are considered critical
16//!         MyErrorKind::Critical.cause(f).into()
17//!     }
18//! }
19//!
20//! # #[allow(dead_code)]
21//! #[derive(Debug, PartialEq, Eq)]
22//! enum MyErrorKind {
23//!     Critical,
24//!     NonCritical,
25//! }
26//! impl ErrorKind for MyErrorKind {}
27//!
28//! fn main() {
29//!     // Tracks an error
30//!     let error: MyError = MyErrorKind::Critical.cause("something wrong").into();
31//!     let error = track!(error);
32//!     let error = track!(error, "I passed here");
33//!     assert_eq!(format!("\nError: {}", error).replace('\\', "/"), r#"
34//! Error: Critical (cause; something wrong)
35//! HISTORY:
36//!   [0] at src/error.rs:27
37//!   [1] at src/error.rs:28 -- I passed here
38//! "#);
39//!
40//!     // Tries to execute I/O operation
41//!     let result = (|| -> Result<_, MyError> {
42//!         let f = track!(std::fs::File::open("/path/to/non_existent_file")
43//!                        .map_err(MyError::from))?;
44//!         Ok(f)
45//!     })();
46//!     let error = result.err().unwrap();
47//!     let cause = error.concrete_cause::<std::io::Error>().unwrap();
48//!     assert_eq!(cause.kind(), std::io::ErrorKind::NotFound);
49//! }
50//! ```
51//!
52//! # `TrackableError` drive macro
53//!
54//! If it is specified (i.e., `#[derive(TrackableError)]`),
55//! the following traits will be automatically implemented in the target error type:
56//! - `Trackable`
57//! - `Error`
58//! - `Display`
59//! - `Deref<Target = TrackableError<$error_kind>>`
60//! - `From<$error_kind>`
61//! - `From<TrackableError<$error_kind>>`
62//! - `From<$target_error_type> for TrackableError<$error_kind>`
63//!
64//! The default value of `$error_kind` is `ErrorKind`.
65//! It can be customized by using `#[trackable(error_type = "$error_kind")]` attribute.
66//!
67//! The target error type must be a newtype (i.e., a tuple struct that has a single element) of `TrackableError`.
68use std::error::Error;
69use std::fmt;
70use std::io;
71use std::sync::Arc;
72
73use super::{Location, Trackable};
74
75/// Boxed `Error` object.
76pub type BoxError = Box<dyn Error + Send + Sync>;
77
78/// Boxed `ErrorKind` object.
79pub type BoxErrorKind = Box<dyn ErrorKind + Send + Sync>;
80
81/// `History` type specialized for `TrackableError`.
82pub type History = ::History<Location>;
83
84/// Built-in `ErrorKind` implementation which represents opaque errors.
85#[derive(Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
86#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
87pub struct Failed;
88impl ErrorKind for Failed {
89    fn description(&self) -> &str {
90        "Failed"
91    }
92}
93
94/// `TrackableError` type specialized for `Failed`.
95#[derive(Debug, Clone, TrackableError)]
96#[trackable(error_kind = "Failed")]
97#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
98pub struct Failure(TrackableError<Failed>);
99impl Failure {
100    /// Makes a new `Failure` instance which was caused by the `error`.
101    pub fn from_error<E>(error: E) -> Self
102    where
103        E: Into<BoxError>,
104    {
105        Failed.cause(error).into()
106    }
107}
108
109/// A variant of `std::io::Error` that implements `Trackable` trait.
110#[derive(Debug, Clone, TrackableError)]
111#[trackable(error_kind = "io::ErrorKind")]
112pub struct IoError(TrackableError<io::ErrorKind>);
113impl From<IoError> for io::Error {
114    fn from(f: IoError) -> Self {
115        io::Error::new(*f.kind(), f)
116    }
117}
118impl From<io::Error> for IoError {
119    fn from(f: io::Error) -> Self {
120        f.kind().cause(f).into()
121    }
122}
123impl From<Failure> for IoError {
124    fn from(f: Failure) -> Self {
125        io::ErrorKind::Other.takes_over(f).into()
126    }
127}
128impl ErrorKind for io::ErrorKind {
129    fn description(&self) -> &str {
130        "I/O Error"
131    }
132}
133
134/// An `Error` type for unit tests.
135pub type TestError = TopLevelError;
136
137/// An `Error` type for `main` function.
138pub type MainError = TopLevelError;
139
140/// An `Error` type for top-level functions.
141pub struct TopLevelError(Box<dyn Error>);
142impl<E: Error + Trackable + 'static> From<E> for TopLevelError {
143    fn from(e: E) -> Self {
144        TopLevelError(Box::new(e))
145    }
146}
147impl fmt::Debug for TopLevelError {
148    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        write!(f, "{}", self.0)
150    }
151}
152impl fmt::Display for TopLevelError {
153    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        write!(f, "{}", self.0)
155    }
156}
157impl Error for TopLevelError {
158    fn source(&self) -> Option<&(dyn Error + 'static)> {
159        Some(&*self.0)
160    }
161}
162
163/// This trait represents an error kind which `TrackableError` can have.
164pub trait ErrorKind: fmt::Debug {
165    /// A short description of the error kind.
166    ///
167    /// This is used for the description of the error that contains it.
168    ///
169    /// The default implementation always returns `"An error"`.
170    fn description(&self) -> &str {
171        "An error"
172    }
173
174    /// Displays this kind.
175    ///
176    /// The default implementation uses the debugging form of this.
177    fn display(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        write!(f, "{:?}", self)
179    }
180}
181impl ErrorKind for String {
182    fn description(&self) -> &str {
183        self
184    }
185}
186
187/// An extention of `ErrorKind` trait.
188///
189/// This provides convenient functions to create a `TrackableError` instance of this kind.
190pub trait ErrorKindExt: ErrorKind + Sized {
191    /// Makes a `TrackableError` instance without cause.
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use std::error::Error;
197    /// use trackable::error::{Failed, ErrorKindExt};
198    ///
199    /// let e = Failed.error();
200    /// assert!(e.cause().is_none());
201    /// ```
202    #[inline]
203    fn error(self) -> TrackableError<Self> {
204        self.into()
205    }
206
207    /// Makes a `TrackableError` instance with the specified `cause`.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use std::error::Error;
213    /// use trackable::error::{Failed, ErrorKindExt};
214    ///
215    /// let e = Failed.cause("something wrong");
216    /// assert_eq!(e.cause().unwrap().to_string(), "something wrong");
217    /// ```
218    #[inline]
219    fn cause<E>(self, cause: E) -> TrackableError<Self>
220    where
221        E: Into<BoxError>,
222    {
223        TrackableError::new(self, cause.into())
224    }
225
226    /// Takes over from other `TrackableError` instance.
227    ///
228    /// The history of `from` will be preserved.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// # #[macro_use]
234    /// # extern crate trackable;
235    /// #
236    /// use trackable::error::{ErrorKind, ErrorKindExt};
237    ///
238    /// #[derive(Debug)]
239    /// struct Kind0;
240    /// impl ErrorKind for Kind0 {}
241    ///
242    /// #[derive(Debug)]
243    /// struct Kind1;
244    /// impl ErrorKind for Kind1 {}
245    ///
246    /// fn main() {
247    ///   let e = Kind0.error();
248    ///   let e = track!(e);
249    ///
250    ///   let e = Kind1.takes_over(e);
251    ///   let e = track!(e);
252    ///
253    ///   assert_eq!(format!("\nERROR: {}", e).replace('\\', "/"), r#"
254    /// ERROR: Kind1
255    /// HISTORY:
256    ///   [0] at src/error.rs:17
257    ///   [1] at src/error.rs:20
258    /// "#);
259    /// }
260    /// ```
261    fn takes_over<F, K>(self, from: F) -> TrackableError<Self>
262    where
263        F: Into<TrackableError<K>>,
264        K: ErrorKind + Send + Sync + 'static,
265    {
266        let from = from.into();
267        TrackableError {
268            kind: self,
269            cause: from.cause,
270            history: from.history,
271        }
272    }
273}
274impl<T: ErrorKind> ErrorKindExt for T {}
275
276/// Trackable error.
277///
278/// # Examples
279///
280/// Defines your own `Error` type.
281///
282/// ```
283/// #[macro_use]
284/// extern crate trackable;
285/// use trackable::error::{TrackableError, ErrorKind, ErrorKindExt};
286///
287/// #[derive(Debug, TrackableError)]
288/// #[trackable(error_kind = "MyErrorKind")]
289/// struct MyError(TrackableError<MyErrorKind>);
290/// impl From<std::io::Error> for MyError {
291///     fn from(f: std::io::Error) -> Self {
292///         // Any I/O errors are considered critical
293///         MyErrorKind::Critical.cause(f).into()
294///     }
295/// }
296///
297/// # #[allow(dead_code)]
298/// #[derive(Debug, PartialEq, Eq)]
299/// enum MyErrorKind {
300///     Critical,
301///     NonCritical,
302/// }
303/// impl ErrorKind for MyErrorKind {}
304///
305/// fn main() {
306///     // Tracks an error
307///     let error: MyError = MyErrorKind::Critical.cause("something wrong").into();
308///     let error = track!(error);
309///     let error = track!(error, "I passed here");
310///     assert_eq!(format!("\nError: {}", error).replace('\\', "/"), r#"
311/// Error: Critical (cause; something wrong)
312/// HISTORY:
313///   [0] at src/error.rs:27
314///   [1] at src/error.rs:28 -- I passed here
315/// "#);
316///
317///     // Tries to execute I/O operation
318///     let result = (|| -> Result<_, MyError> {
319///         let f = track!(std::fs::File::open("/path/to/non_existent_file")
320///                        .map_err(MyError::from))?;
321///         Ok(f)
322///     })();
323///     let error = result.err().unwrap();
324///     let cause = error.concrete_cause::<std::io::Error>().unwrap();
325///     assert_eq!(cause.kind(), std::io::ErrorKind::NotFound);
326/// }
327/// ```
328///
329/// `TrackableError` is cloneable if `K` is so.
330///
331/// ```no_run
332/// #[macro_use]
333/// extern crate trackable;
334///
335/// use trackable::Trackable;
336/// use trackable::error::{Failed, ErrorKindExt};
337///
338/// fn main() {
339///     let mut original = Failed.error();
340///
341///     let original = track!(original, "Hello `original`!");
342///     let forked = original.clone();
343///     let forked = track!(forked, "Hello `forked`!");
344///
345///     assert_eq!(format!("\n{}", original).replace('\\', "/"), r#"
346/// Failed
347/// HISTORY:
348///   [0] at src/error.rs:11 -- Hello `original`!
349/// "#);
350///
351///     assert_eq!(format!("\n{}", forked).replace('\\', "/"), r#"
352/// Failed
353/// HISTORY:
354///   [0] at src/error.rs:11 -- Hello `original`!
355///   [1] at src/error.rs:13 -- Hello `forked`!
356/// "#);
357/// }
358/// ```
359#[derive(Debug, Clone)]
360#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
361pub struct TrackableError<K> {
362    kind: K,
363    cause: Option<Cause>,
364    history: History,
365}
366impl<K: ErrorKind> TrackableError<K> {
367    /// Makes a new `TrackableError` instance.
368    pub fn new<E>(kind: K, cause: E) -> Self
369    where
370        E: Into<BoxError>,
371    {
372        TrackableError {
373            kind,
374            cause: Some(Cause(Arc::new(cause.into()))),
375            history: History::new(),
376        }
377    }
378
379    /// Makes a new `TrackableError` instance from `kind`.
380    ///
381    /// Note that the returning error has no cause.
382    fn from_kind(kind: K) -> Self {
383        TrackableError {
384            kind,
385            cause: None,
386            history: History::new(),
387        }
388    }
389
390    /// Returns the kind of this error.
391    #[inline]
392    pub fn kind(&self) -> &K {
393        &self.kind
394    }
395
396    /// Tries to return the cause of this error as a value of `T` type.
397    ///
398    /// If neither this error has a cause nor it is an `T` value,
399    /// this method will return `None`.
400    #[inline]
401    pub fn concrete_cause<T>(&self) -> Option<&T>
402    where
403        T: Error + 'static,
404    {
405        self.cause.as_ref().and_then(|c| c.0.downcast_ref())
406    }
407}
408impl<K: ErrorKind> From<K> for TrackableError<K> {
409    #[inline]
410    fn from(kind: K) -> Self {
411        Self::from_kind(kind)
412    }
413}
414impl<K: ErrorKind + Default> Default for TrackableError<K> {
415    #[inline]
416    fn default() -> Self {
417        Self::from_kind(K::default())
418    }
419}
420impl<K: ErrorKind> fmt::Display for TrackableError<K> {
421    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
422        self.kind.display(f)?;
423        if let Some(ref e) = self.cause {
424            write!(f, " (cause; {})", e.0)?;
425        }
426        write!(f, "\n{}", self.history)?;
427        Ok(())
428    }
429}
430impl<K: ErrorKind> Error for TrackableError<K> {
431    fn description(&self) -> &str {
432        self.kind.description()
433    }
434    fn cause(&self) -> Option<&dyn Error> {
435        self.cause.as_ref().map::<&dyn Error, _>(|e| &**e.0)
436    }
437}
438impl<K> Trackable for TrackableError<K> {
439    type Event = Location;
440
441    #[inline]
442    fn history(&self) -> Option<&History> {
443        Some(&self.history)
444    }
445
446    #[inline]
447    fn history_mut(&mut self) -> Option<&mut History> {
448        Some(&mut self.history)
449    }
450}
451
452#[derive(Debug, Clone)]
453struct Cause(Arc<BoxError>);
454
455#[cfg(feature = "serialize")]
456mod impl_serde {
457    use serde::{Deserialize, Deserializer, Serialize, Serializer};
458    use std::sync::Arc;
459
460    use super::Cause;
461
462    impl Serialize for Cause {
463        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
464        where
465            S: Serializer,
466        {
467            serializer.serialize_str(&self.0.to_string())
468        }
469    }
470    impl<'de> Deserialize<'de> for Cause {
471        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
472        where
473            D: Deserializer<'de>,
474        {
475            let s = String::deserialize(deserializer)?;
476            Ok(Cause(Arc::new(s.into())))
477        }
478    }
479}
480
481#[cfg(test)]
482mod test {
483    use super::*;
484    use std;
485
486    #[test]
487    fn it_works() {
488        #[derive(Debug, TrackableError)]
489        #[trackable(error_kind = "MyErrorKind")]
490        struct MyError(TrackableError<MyErrorKind>);
491        impl From<std::io::Error> for MyError {
492            fn from(f: std::io::Error) -> Self {
493                // Any I/O errors are considered critical
494                MyErrorKind::Critical.cause(f).into()
495            }
496        }
497
498        #[allow(dead_code)]
499        #[derive(Debug, PartialEq, Eq)]
500        enum MyErrorKind {
501            Critical,
502            NonCritical,
503        }
504        impl ErrorKind for MyErrorKind {}
505
506        // Tracks an error
507        let error: MyError = MyErrorKind::Critical.cause("something wrong").into();
508        let error = track!(error);
509        let error = track!(error, "I passed here");
510        assert_eq!(
511            format!("\nError: {}", error).replace('\\', "/"),
512            r#"
513Error: Critical (cause; something wrong)
514HISTORY:
515  [0] at src/error.rs:508
516  [1] at src/error.rs:509 -- I passed here
517"#
518        );
519
520        // Tries to execute I/O operation
521        let result = (|| -> Result<_, MyError> {
522            let f =
523                track!(std::fs::File::open("/path/to/non_existent_file").map_err(MyError::from,))?;
524            Ok(f)
525        })();
526        let error = result.err().unwrap();
527        let cause = error.concrete_cause::<std::io::Error>().unwrap();
528        assert_eq!(cause.kind(), std::io::ErrorKind::NotFound);
529    }
530}