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}