1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//! This crate provides a single trait, `ErrWith`, with one method,
//! `err_with`. `ErrWith` is implemented for `Result<T, E>`, with
//! `result.err_with(w)` transforming an `Err(e)` to an `Err((e,w))`,
//! and leaving an `Ok(...)` unchanged.
//!
//! This is not particularly useful on its own, but can be used to
//! define conversions from `(E, W)` into your custom error types, so
//! error contexts can be recorded and reported later.
//!
//! For example:
//!
//! ```no_run
//! use std::{fs, io, path::{Path, PathBuf}};
//!
//! use err_with::ErrWith;
//!
//! // Given this custom error type:
//! #[derive(Debug)]
//! enum Error {
//!   Io { io_error: io::Error, path: PathBuf },
//! }
//!
//! // We can define a conversion from `(io::Error, AsRef<Path>)` to our
//! // custom error type:
//! impl<P: AsRef<Path>> From<(io::Error, P)> for Error {
//!     fn from((io_error, path): (io::Error, P)) -> Error {
//!         Error::Io {
//!             path: path.as_ref().to_owned(),
//!             io_error,
//!         }
//!     }
//! }
//!
//! // Which allows us to attach the path of an I/O error and convert
//! // the error into our custom error type in an ergonomic fashion:
//! fn main() -> Result<(), Error> {
//!     fs::read_to_string("foo/bar").err_with("foo/bar")?;
//!     Ok(())
//! }
//! ```
//!

pub trait ErrWith<T, E> {
  fn err_with<W>(self, with: W) -> Result<T, (E, W)>;
}

impl<T, E> ErrWith<T, E> for Result<T, E> {
  fn err_with<W>(self, with: W) -> Result<T, (E, W)> {
    match self {
      Ok(ok) => Ok(ok),
      Err(error) => Err((error, with)),
    }
  }
}