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}