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
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#![deny(missing_docs)]

//! Print errors with [`Display`] instead of [`Debug`] when using `?` in `main()`.
//!
//! Use by returning [`MainError`] as the error type from `main()`.
//! Example:
//!
//! ```should_panic
//! use main_error::MainError;
//!
//! fn main() -> Result<(), MainError> {
//!     // This prints
//!     //   "Error: invalid digit found in string"
//!     // instead of (if you used `Result<(), Box<dyn Error>>` or similar)
//!     //   "ParseIntError { kind: InvalidDigit }".
//!     let number: i32 = "not a number".parse()?;
//!
//!     Ok(())
//! }
//! ```
//!
//! For convenience, you can also use the [`MainResult`] type.
//! See below for more details.
//!
//! # The Issue
//!
//! Since [Rust 1.26](https://blog.rust-lang.org/2018/05/10/Rust-1.26.html#main-can-return-a-result), `main()` function can return a [`Result<T, E>`](core::result).
//! This enables the use of `?` for convenient error handling in small programs and quick examples ([RFC](https://github.com/rust-lang/rfcs/pull/1937)).
//!
//! Unfortunately, the error is printed via [`Debug`] ([hardcoded in the standard library](https://doc.rust-lang.org/src/std/process.rs.html), search for "Error:"),
//! which gives not very pretty or human-friendly output.
//!
//! For example, this program:
//!
//! ```should_panic
//! # use std::num::ParseIntError;
//! fn main() -> Result<(), ParseIntError> {
//!     let num: i32 = "not a number".parse()?;
//!     // ...
//! #     Ok(())
//! }
//! ```
//!
//! will print
//!
//! ```text
//! Error: ParseIntError { kind: InvalidDigit }
//! ```
//!
//! # Solution
//!
//! This crate provides [`MainError`] as a drop-in replacement for the error type `E` in your `main`'s `Result<T, E>`.
//! It prints the error via [`Display`] instead of [`Debug`], which yields a nicer error message.
//! For example, the program above can be changed to
//!
//! ```should_panic
//! use main_error::MainError;
//!
//! fn main() -> Result<(), MainError> {
//!     let _: i32 = "not a number".parse()?;
//!     // ...
//! #    Ok(())
//! }
//! ```
//!
//! and now prints:
//! ```output
//! Error: invalid digit found in string
//! ```
//!
//! # Details and Drawbacks
//!
//! - [`MainError`] stores the original error as `Box<dyn Error>`.
//!   This incurs one allocation (on conversion) and one virtual call (on printing).
//!   Since there can be exactly one error like this before the program ends, this cost is insignificant.
//! - [`MainError`] implements [`From`] for all types that can be converted into a `Box<dyn Error>`.
//!     1. This allows it to be used in place of any type that implements the [`Error`] trait (see example above).
//!     2. It can also be used in place of any type that can be _converted_ to a `Box<dyn Error>`, e.g., `String`.
//! - [`MainError`] does not implement the [`Error`] trait itself. Reasons:
//!     1. It's not necessary, because the standard library only requires `E: Debug` for `main() -> Result<T, E>`.
//!     2. You should only be using `MainError` for `main()` anyway, whereas the `Error` trait is more for interoparability between libraries.
//!     3. One simply _cannot_ implement `Error` for `MainError`, because this would create an overlapping `impl`.  
//!        Explanation:  
//!        - `MainError` can be converted from a `T: Into<Box<dyn Error>>`.
//!        - `Into<Box<dyn Error>>` [is implemented](std::error::Error#implementors) for `E: Error` itself.
//!        - If `MainError` would implement `Error`, it could be converted from itself.
//!        - This collides with the [reflexive `impl<T> From<T> for T` in core](core::convert::From#generic-implementations).
//! - [`MainError`] implements [`Debug`] in terms of [`Display`] of the underlying error.
//!   This is hacky, but unfortunately [`Debug`] as the output for the `main` error case is stable now.
//!   The `"Error: "` part at the beginning of the output comes [from the standard library](https://doc.rust-lang.org/src/std/process.rs.html), thus it cannot be changed.

use std::error::Error;
use std::fmt::{self, Debug, Display};

/// Newtype wrapper around a boxed [`std::error::Error`].
/// - It implements [`Debug`] so that it can be used in `fn main() -> Result<(), MainError>`.
/// - It implements [`From<E>`](From) for `E: Into<Box<dyn Error>>` so that it works as a drop-in for any type that can be converted into a boxed [`Error`] (i.e., an `Error` trait object).
///
/// `MainError` can only be constructed through its [`From`] impl:
/// Explicitly with `from`/`into` or implicitly through the `?` operator.
///
/// # Example
///
/// Explicit construction via `MainError::from`:
/// ```
/// # use main_error::MainError;
/// let e = MainError::from("something convertible to Box<dyn Error>");
/// ```
///
/// Or via `into()` when the target type can be inferred from the context:
/// ```should_panic
/// # use main_error::MainError;
/// fn main() -> Result<(), MainError> {
///     Err("something convertible to Box<dyn Error>".into())
/// }
/// ```
///
/// Or even easier via `?`:
/// ```should_panic
/// # use main_error::MainError;
/// fn main() -> Result<(), MainError> {
///     Err("something convertible to Box<dyn Error>")?
/// }
/// ```
pub struct MainError(Box<dyn Error>);

impl<E: Into<Box<dyn Error>>> From<E> for MainError {
    fn from(e: E) -> Self {
        MainError(e.into())
    }
}

// impl Debug (to satisfy trait bound for main()-Result error reporting), but use Display of wrapped
// error internally (for nicer output).
impl Debug for MainError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Display::fmt(&self.0, f)?;
        let mut source = self.0.source();
        while let Some(error) = source {
            write!(f, "\ncaused by: {}", error)?;
            source = error.source();
        }
        Ok(())
    }
}

/// Convenience type as a shorthand return type for `main()`.
pub type MainResult = Result<(), MainError>;