err_context/
lib.rs

1#![doc(
2    html_root_url = "https://docs.rs/err-context/0.1.0/err-context/",
3    test(attr(deny(warnings)))
4)]
5#![forbid(unsafe_code)]
6#![warn(missing_docs)]
7
8//! Lightweight error context manipulation.
9//!
10//! Oftentimes, when error is being reported to the user, the lowest-level cause of the original
11//! error is not very interesting (eg. „No such file or directory“). It is not clear what file it
12//! should have been, why the program needed that file and what the consequences of failing to find
13//! it are.
14//!
15//! This library allows to wrap the low-level error into layers of context easily and examine such
16//! multi-layer libraries. Depending on preferences of the developer, the story of the whole error
17//! might then be found either in the outer layer, or the whole chain may be needed.
18//!
19//! There are other libraries with similar aim, though none seemed to fit very well. Most of them
20//! have unergonomic API. This API is modeled after the [`failure`] crate (which has simple and
21//! powerful API), but uses the [`Error`] trait in standard library.
22//!
23//! # Compatibility
24//!
25//! By using the trait and [`AnyError`] type alias, the library is compatible with any future or
26//! past version of self or any other error handling library that operates on the [`Error`] trait.
27//! To avoid dependencies on specific version of this library, downstream crates are advised to not
28//! reexport types from here with the sole exception of the [`AnyError`] alias (as it is alias, it
29//! can be re-created independently by as many libraries and is compatible). Downstream libraries
30//! are, of course, free to expose their own errors.
31//!
32//! # Using the library
33//!
34//! Besides the [`AnyError`] alias, users of the library probably don't want to use any of the
35//! present types here *directly*. Instead, certain traits can be imported and all errors, boxed
36//! errors and results containing them get additional methods.
37//!
38//! The methods can be found on the [`ErrorExt`] and [`ResultExt`] traits.
39//!
40//! ## Producing errors
41//!
42//! The lowest level error comes from somewhere else ‒ it may be type provided by some other crate,
43//! an error implemented manually or generated with help of some other crate (the [`err-derive`]
44//! crate offers derive macro similar to the one of [`failure`], but for standard errors ‒
45//! combination of these two crates provide almost all the functionality of [`failure`]).
46//!
47//! Then, as the error bubbles up, it can be enriched by additional information using the
48//! [`.context`][ErrorExt::context] and [`.with_context`][ResultExt::context] methods.
49//!
50//! ```
51//! use std::error::Error;
52//! use std::io::Read;
53//! use std::fmt::{Display, Formatter, Result as FmtResult};
54//! use std::fs::File;
55//! use std::path::Path;
56//!
57//! use err_context::AnyError;
58//! use err_context::prelude::*;
59//!
60//! // An example error. You'd implement it manually like this, or use something else, like
61//! // err-derive, to generate it.
62//! #[derive(Debug)]
63//! struct BrokenImage;
64//!
65//! impl Display for BrokenImage {
66//!     fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
67//!         write!(fmt, "The image is broken")
68//!     }
69//! }
70//!
71//! impl Error for BrokenImage {}
72//!
73//! #[derive(Clone, Debug)]
74//! struct Image;
75//!
76//! impl Image {
77//!     fn parse(_: Vec<u8>) -> Result<Self, BrokenImage> {
78//!         // This is an example. We didn't really implement this.
79//!         Err(BrokenImage)
80//!     }
81//! }
82//!
83//! fn read_image<P: AsRef<Path>>(path: P) -> Result<Image, AnyError> {
84//!     let mut file = File::open(&path)
85//!         .with_context(|_| format!("Couldn't open file {}", path.as_ref().display()))?;
86//!     let mut data = Vec::new();
87//!     file.read_to_end(&mut data)
88//!         .with_context(|_| format!("File {} couldn't be read", path.as_ref().display()))?;
89//!     let image = Image::parse(data)
90//!         .with_context(|_| format!("Image in file {} is corrupt", path.as_ref().display()))?;
91//!     Ok(image)
92//! }
93//!
94//! # fn main() { read_image("/dev/null").unwrap_err(); }
95//! ```
96//!
97//! Note that the type of the error produced by any of these methods doesn't carry any useful
98//! information. Therefore this library should be used only at places where the error is meant for
99//! printing out to user or some other handling in uniform manner. Libraries providing building
100//! blocks might want to implement their own typed errors, with possibly usefully typed layers.
101//!
102//! ## Consuming the errors
103//!
104//! These kinds of errors are usually meant for the user. The outer layer's [`Display`] contains
105//! only its own context, eg:
106//!
107//! ```
108//! # use err_context::prelude::*;
109//! let inner = std::io::Error::last_os_error();
110//! let outer = inner.context("Some error");
111//! assert_eq!("Some error", outer.to_string());
112//! ```
113//!
114//! If you're interested in all the layers, they can be iterated (this simply calls the
115//! [`source`][Error::source] method until it gets to the bottom).
116//!
117//! ```
118//! # use err_context::prelude::*;
119//! let inner = std::io::Error::last_os_error();
120//! let outer = inner.context("Some error");
121//! // This will print
122//! //   Some error
123//! //   success (or whatever the last io error was)
124//! for e in outer.chain() {
125//!     println!("{}", e);
126//! }
127//! ```
128//!
129//! Or, more conveniently can be printed as a single string:
130//!
131//! ```
132//! # use err_context::prelude::*;
133//! let inner = std::io::Error::last_os_error();
134//! let outer = inner.context("Some error");
135//! // Will print something like:
136//! // Some error: success
137//! println!("{}", outer.display(", "));
138//! ```
139//!
140//! Despite being mostly aimed for human output, the chain still can be examined to an extend. In
141//! particular, it is possible to look for the outermost error in the chain of specific type. This
142//! will find the inner error.
143//!
144//! ```
145//! # use err_context::prelude::*;
146//! let inner = std::io::Error::last_os_error();
147//! let outer = inner.context("Some error");
148//! assert!(outer.find_source::<std::io::Error>().is_some());
149//! ```
150//!
151//! [`failure`]: https://crates.io/crates/failure
152//! [`err-derive`]: https://crates.io/crates/err-derive
153
154use std::error::Error;
155use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
156use std::mem;
157
158/// A type alias for boxed errors.
159///
160/// By convention, errors should be Send and Sync (because they are usually just dumb values).
161/// Everything in the crate should work even with similar types without these bounds.
162///
163/// Re-defining the type alias (or using it without the type alias) works too. This is just
164/// convenience and self-explanation of code.
165///
166/// It is possible a similar type alias will eventually get added to the standard library. If so,
167/// the crate will be automatically compatible with that too.
168pub type AnyError = Box<dyn Error + Send + Sync>;
169
170/// An iterator through the error chain.
171///
172/// Produced by the [chain][ErrorExt::chain] method.
173#[derive(Copy, Clone, Debug)]
174pub struct Chain<'a>(Option<&'a (dyn Error + 'static)>);
175
176impl<'a> Iterator for Chain<'a> {
177    type Item = &'a (dyn Error + 'static);
178    fn next(&mut self) -> Option<Self::Item> {
179        let current = self.0.take();
180        if let Some(current) = current {
181            self.0 = current.source();
182        }
183        current
184    }
185}
186
187/// A display implementation for formatting separated layers of an error.
188///
189/// Produced by the [`display`][ErrorExt::display] method.
190#[derive(Copy, Clone, Debug)]
191pub struct DisplayChain<'a, S> {
192    chain: Chain<'a>,
193    separator: S,
194}
195
196impl<S: Display> Display for DisplayChain<'_, S> {
197    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
198        let mut started = false;
199        // Note: iteration takes &mut, but Chain is copy, so we iterate over a copy.
200        for level in self.chain {
201            if mem::replace(&mut started, true) {
202                write!(fmt, "{}", self.separator)?;
203            }
204            write!(fmt, "{}", level)?;
205        }
206        Ok(())
207    }
208}
209
210/// Additional level of context, wrapping some error inside itself.
211///
212/// This is produced by the [`context`][ErrorExt::context] and implements the additional (outer)
213/// layer in the chain. Any number of contexts can be chained together.
214#[derive(Copy, Clone, Debug)]
215pub struct Context<M, E> {
216    msg: M,
217    inner: E,
218}
219
220impl<M: Display, E> Display for Context<M, E> {
221    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
222        self.msg.fmt(fmt)
223    }
224}
225
226impl<M: Debug + Display, E: Error + 'static> Error for Context<M, E> {
227    fn source(&self) -> Option<&(dyn Error + 'static)> {
228        Some(&self.inner)
229    }
230}
231
232impl<M, E> Context<M, E> {
233    /// A direct constructor for the context.
234    ///
235    /// More usually created by the [`context`][ErrorExt::context], but allowing for construction
236    /// directly without importing the trait.
237    pub fn new(msg: M, error: E) -> Self {
238        Self { msg, inner: error }
239    }
240
241    /// Extracts the inner error, peeling off the outer layer.
242    pub fn into_inner(self) -> E {
243        self.inner
244    }
245}
246
247/// Additional context for boxed errors.
248///
249/// This has the same purpose and the same functionality as [`Context`]. It is a separate type for
250/// technical reasons in implementation.
251#[derive(Debug)]
252pub struct BoxContext<M, E: ?Sized> {
253    msg: M,
254    inner: Box<E>,
255}
256
257impl<M, E: ?Sized> BoxContext<M, E> {
258    /// Direct construction of the context.
259    pub fn new(msg: M, error: Box<E>) -> Self {
260        Self { msg, inner: error }
261    }
262
263    /// Extracts the inner error, peeling off the outer layer.
264    pub fn into_inner(self) -> Box<E> {
265        self.inner
266    }
267}
268
269impl<M: Display, E: ?Sized> Display for BoxContext<M, E> {
270    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
271        self.msg.fmt(fmt)
272    }
273}
274
275macro_rules! impl_err {
276    ($ty: ty) => {
277        impl<M: Debug + Display> Error for BoxContext<M, $ty> {
278            fn source(&self) -> Option<&(dyn Error + 'static)> {
279                Some(&*self.inner)
280            }
281        }
282    };
283}
284
285impl_err!(dyn Error + Send + Sync);
286impl_err!(dyn Error + Send);
287impl_err!(dyn Error + Sync);
288impl_err!(dyn Error);
289
290#[cfg(feature = "failure")]
291impl<M> Context<M, failure::Compat<failure::Error>> {
292    /// Constructor of context from a failure's [`Error`][failure::Error].
293    ///
294    /// This is a compatibility constructor, for wrapping the error of failure. It is enabled by
295    /// the `failure` feature.
296    ///
297    /// # Warning
298    ///
299    /// The compatibility layer has no way to access the original causes of the failure. Therefore,
300    /// the inner layers of the provided failure will be lost and the failure will be considered
301    /// the innermost level.
302    pub fn from_failure(msg: M, failure: failure::Error) -> Self {
303        Self::new(msg, failure.compat())
304    }
305}
306
307#[cfg(feature = "failure")]
308impl<M, F: failure::Fail> Context<M, failure::Compat<F>> {
309    /// Constructor of context from a failure's [`Fail`][failure::Fail].
310    ///
311    /// This is a compatibility constructor, for wrapping failure. It is enabled by the `failure`
312    /// feature.
313    ///
314    /// # Warning
315    ///
316    /// The compatibility layer has no way to access the original causes of the failure. Therefore,
317    /// the inner layers of the provided failure will be lost and the failure will be considered
318    /// the innermost level.
319    pub fn from_fail(msg: M, failure: F) -> Self {
320        Self::new(msg, failure.compat())
321    }
322}
323
324/// Extension trait for the [`Error`] trait.
325///
326/// This adds additional methods to all the [`Error`] types. See the [crate
327/// documentation](index.html) for examples and general principles.
328///
329/// Note that usually this trait is not imported directly, but through the [`prelude`].
330pub trait ErrorExt: private::Sealed + Sized {
331    /// Wraps the error into another layer of context.
332    ///
333    /// The produced error will have one more layer. The outer layer will have the provided message
334    /// as its [`Display`] implementation.
335    fn context<M: Display>(self, msg: M) -> Context<M, Self>;
336
337    /// Iterates over all the layers of the error.
338    ///
339    /// The first entry will be the outer layer (`self`), the last one the lowest-level/innermost
340    /// source. Therefore this iterator is never empty. It iterates by reference.
341    fn chain(&self) -> Chain<'_>
342    where
343        Self: 'static;
344
345    /// Looks for an outermost error of the given type.
346    ///
347    /// This is combination of iteration and downcasting of the errors. The returned value is
348    /// reference to the outermost error layer that matches given type, as the given type.
349    ///
350    /// The type of the context layers is often very uninteresting, but the code might want to find
351    /// some specific error in there. This allows skipping the unknown number of human-readable
352    /// „comments“ and get to the facts. Note that it doesn't have to be the lowest-level one ‒
353    /// even properly typed errors can have their sources.
354    fn find_source<T: Error + 'static>(&self) -> Option<&T>
355    where
356        Self: 'static,
357    {
358        self.chain().find_map(|e| e.downcast_ref::<T>())
359    }
360
361    /// Returns a [`Display`] representation of the whole chain of errors.
362    ///
363    /// This can be used to output the whole chain (as opposed to just outputting the error
364    /// directly). The layers are separated by the provided `separator`.
365    fn display<S: Display>(&self, separator: S) -> DisplayChain<'_, S>
366    where
367        Self: 'static,
368    {
369        DisplayChain {
370            chain: self.chain(),
371            separator,
372        }
373    }
374}
375
376impl<E: Error> private::Sealed for E {}
377
378impl<E: Error> ErrorExt for E {
379    fn context<M: Display>(self, msg: M) -> Context<M, Self> {
380        Context::new(msg, self)
381    }
382    fn chain(&self) -> Chain<'_>
383    where
384        Self: 'static,
385    {
386        Chain(Some(self))
387    }
388}
389
390/// An equivalent of [`ErrorExt`] for boxed errors.
391///
392/// This is effectively the same trait as [`ErrorExt`], but for boxed errors. It exists separately
393/// purely for implementation reasons.
394pub trait BoxedErrorExt<E: ?Sized>: private::BoxedSealed + Sized {
395    /// Equivalent of [`ErrorExt::context`].
396    fn context<M: Display>(self, msg: M) -> BoxContext<M, E>;
397    /// Equivalent of [`ErrorExt::chain`].
398    fn chain(&self) -> Chain<'_>;
399    /// Equivalent of [`ErrorExt::find_source`].
400    fn find_source<T: Error + 'static>(&self) -> Option<&T> {
401        self.chain().find_map(|e| e.downcast_ref::<T>())
402    }
403    /// Equivalent of [`ErrorExt::display`].
404    fn display<S: Display>(&self, separator: S) -> DisplayChain<'_, S> {
405        DisplayChain {
406            chain: self.chain(),
407            separator,
408        }
409    }
410}
411
412macro_rules! impl_any_error {
413    ($ty: ty) => {
414        impl private::BoxedSealed for Box<$ty> {}
415        impl BoxedErrorExt<$ty> for Box<$ty> {
416            fn context<M: Display>(self, msg: M) -> BoxContext<M, $ty> {
417                BoxContext::new(msg, self)
418            }
419            fn chain(&self) -> Chain<'_> {
420                Chain(Some(&**self))
421            }
422        }
423    };
424}
425
426impl_any_error!(dyn Error + Send + Sync);
427impl_any_error!(dyn Error + Send);
428impl_any_error!(dyn Error + Sync);
429impl_any_error!(dyn Error);
430
431/// Extension traits for results.
432///
433/// This provides method to enrich the error in the result with additional context. See the general
434/// principles and examples at the [crate level documentation](index.html).
435///
436/// Usually, this trait isn't imported directly, but through the [`prelude`].
437pub trait ResultExt<T, E>: private::ResultSealed + Sized {
438    /// Wraps the error in another layer of context.
439    ///
440    /// If the result is success, this does nothing. If it is error, it wraps the error in another
441    /// layer, in the same way as calling [`.context`][ErrorExt::context] on that error itself
442    /// would.
443    ///
444    /// If the construction of the message is expensive, consider using
445    /// [`with_context`][ResultExt::with_context].
446    fn context<M>(self, msg: M) -> Result<T, Context<M, E>>
447    where
448        M: Display;
449
450    /// Wraps the error in another layer of context.
451    ///
452    /// This works in a similar way as [`context`][ResultExt::context]. However, the closure to
453    /// construct the context is called only in case the result is `Err`, avoiding the construction
454    /// cost in the success case.
455    ///
456    /// As the common usage goes, string literal can be passed directly with
457    /// [`context`][ResultExt::context], but calling `format!` to construct the message would be
458    /// better done with this method.
459    fn with_context<F, M>(self, f: F) -> Result<T, Context<M, E>>
460    where
461        F: FnOnce(&E) -> M,
462        M: Display;
463}
464
465impl<T, E: Error> private::ResultSealed for Result<T, E> {}
466
467impl<T, E: Error> ResultExt<T, E> for Result<T, E> {
468    fn context<M>(self, msg: M) -> Result<T, Context<M, E>>
469    where
470        M: Display,
471    {
472        self.map_err(|e| e.context(msg))
473    }
474
475    fn with_context<F, M>(self, f: F) -> Result<T, Context<M, E>>
476    where
477        F: FnOnce(&E) -> M,
478        M: Display,
479    {
480        self.map_err(|e| {
481            let msg = f(&e);
482            e.context(msg)
483        })
484    }
485}
486
487/// A [`ResultExt`] equivalent for boxed errors.
488///
489/// This trait serves the same purpose and acts in the same ways as the [`ResultExt`], so refer to
490/// that for details. It exists merely for implementation purposes.
491pub trait BoxedResultExt<T, E: ?Sized>: private::BoxedResultSealed {
492    /// A [`ResultExt::context`] equivalent.
493    fn context<M>(self, msg: M) -> Result<T, BoxContext<M, E>>
494    where
495        M: Display;
496
497    /// A [`ResultExt::with_context`] equivalent.
498    fn with_context<F, M>(self, f: F) -> Result<T, BoxContext<M, E>>
499    where
500        F: FnOnce(&Box<E>) -> M,
501        M: Display;
502}
503
504macro_rules! any_result_impl {
505    ($ty: ty) => {
506        impl<T> private::BoxedResultSealed for Result<T, Box<$ty>> {}
507        impl<T> BoxedResultExt<T, $ty> for Result<T, Box<$ty>> {
508            fn context<M>(self, msg: M) -> Result<T, BoxContext<M, $ty>>
509            where
510                M: Display,
511            {
512                self.map_err(|e| e.context(msg))
513            }
514
515            fn with_context<F, M>(self, f: F) -> Result<T, BoxContext<M, $ty>>
516            where
517                F: FnOnce(&Box<$ty>) -> M,
518                M: Display,
519            {
520                self.map_err(|e| {
521                    let msg = f(&e);
522                    e.context(msg)
523                })
524            }
525        }
526    };
527}
528
529any_result_impl!(dyn Error + Send + Sync);
530any_result_impl!(dyn Error + Send);
531any_result_impl!(dyn Error + Sync);
532any_result_impl!(dyn Error);
533
534/// A module with reexports to wildcard-import all relevant traits.
535///
536/// The library adds methods to existing types by extension traits. For them to work, they need to
537/// be in scope. It is more convenient to import them all from prelude instead of individually.
538///
539/// ```
540/// # #[allow(unused_imports)]
541/// use err_context::prelude::*;
542/// ```
543///
544/// Only the traits are imported and they are imported anonymously (so their names can't clash with
545/// anything, but they also can't be referred directly).
546pub mod prelude {
547    pub use crate::BoxedErrorExt as _;
548    pub use crate::BoxedResultExt as _;
549    pub use crate::ErrorExt as _;
550    pub use crate::ResultExt as _;
551}
552
553mod private {
554    // Trick to prevent others implementing our extension traits. This allows us adding new methods
555    // in the future without breaking API.
556    pub trait Sealed {}
557    pub trait BoxedSealed {}
558    pub trait ResultSealed {}
559    pub trait BoxedResultSealed {}
560}
561
562#[cfg(test)]
563mod tests {
564    use super::*;
565    use std::io::{Error as IoError, Read};
566
567    fn _context_error() -> impl Error {
568        IoError::last_os_error().context("Hello")
569    }
570
571    fn _context_any_error() -> impl Error {
572        let e: AnyError = IoError::last_os_error().into();
573        e.context(42)
574    }
575
576    fn _context_result() -> Result<(), AnyError> {
577        let mut buf = [0];
578        std::io::stdin()
579            .read(&mut buf)
580            .context("Failed to read line")?;
581        Ok(())
582    }
583
584    fn _double_context() -> Result<(), AnyError> {
585        _context_result().with_context(|e| format!("Hey: {}", e))?;
586        Ok(())
587    }
588
589    #[derive(Copy, Clone, Debug)]
590    struct Dummy;
591
592    impl Display for Dummy {
593        fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
594            write!(fmt, "Dummy error!")
595        }
596    }
597
598    impl Error for Dummy {}
599
600    fn get_chain() -> AnyError {
601        Dummy.context("Sorry").into()
602    }
603
604    fn get_boxed() -> AnyError {
605        Dummy.into()
606    }
607
608    fn get_boxed_chain() -> AnyError {
609        let a = get_boxed().context("Sorry");
610        a.into()
611    }
612
613    #[test]
614    fn iter_chain() {
615        assert_eq!(1, Dummy.chain().count());
616        assert_eq!(2, get_chain().chain().count());
617    }
618
619    #[test]
620    fn find_dummy() {
621        assert!(Dummy.find_source::<Dummy>().is_some());
622        assert!(get_chain().find_source::<Dummy>().is_some());
623        assert!(get_boxed_chain().find_source::<Dummy>().is_some());
624        assert!(get_chain().find_source::<IoError>().is_none());
625    }
626
627    #[test]
628    fn display_chain() {
629        let chain = get_chain();
630        assert_eq!("Sorry", chain.to_string());
631        assert_eq!("Sorry: Dummy error!", chain.display(": ").to_string());
632    }
633}