1 // Copyright (c) 2018 Toby Smith <toby@tismith.id.au>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/license/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 #![deny(
10     missing_docs,
11     missing_debug_implementations,
12     missing_copy_implementations,
13     trivial_casts,
14     trivial_numeric_casts,
15     unreachable_pub,
16     unsafe_code,
17     unstable_features,
18     unused_extern_crates,
19     unused_import_braces,
20     unused_qualifications,
21     variant_size_differences
22 )]
23 
24 //! Some newtype wrappers to help with using ? in main()
25 //!
26 //! The primary items exported by this library are:
27 //!
28 //! - `ExitFailure`: a wrapper around `failure::Error` to allow ? printing from main
29 //!    to present a nicer error message, including any available context and backtrace.
30 //!
31 //! - `ExitDisplay<E>`: a wrapper around `E: std::fmt::Display` to allow the error message
32 //!    from main to use `Display` and not `Debug`
33 //!
34 //! Basically, these types should only ever be used in the return type for
35 //! `main()`
36 //!
37 extern crate failure;
38 
39 /// The newtype wrapper around `failure::Error`
40 ///
41 /// ```rust,should_panic
42 /// # extern crate failure;
43 /// # extern crate exitfailure;
44 /// # use failure::ResultExt;
45 /// # use exitfailure::ExitFailure;
46 /// fn main() -> Result<(), ExitFailure> {
47 ///     Ok(some_fn()?)
48 /// }
49 ///
50 /// fn some_fn() -> Result<(), failure::Error> {
51 ///     let error = Err(failure::err_msg("root cause failure"));
52 ///     Ok(error.context("this is some context".to_string())?)
53 /// }
54 /// ```
55 pub struct ExitFailure(failure::Error);
56 
57 /// Prints a list of causes for this Error, along with any backtrace
58 /// information collected by the Error (if RUST_BACKTRACE=1).
59 impl std::fmt::Debug for ExitFailure {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result60     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
61         let fail = self.0.as_fail();
62         write!(f, "{}", fail)?;
63 
64         for cause in fail.iter_causes() {
65             write!(f, "\nInfo: caused by {}", cause)?;
66         }
67 
68         if let Ok(x) = std::env::var("RUST_BACKTRACE") {
69             if x != "0" {
70                 write!(f, "\n{}", self.0.backtrace())?
71             }
72         }
73 
74         Ok(())
75     }
76 }
77 
78 impl<T: Into<failure::Error>> From<T> for ExitFailure {
from(t: T) -> Self79     fn from(t: T) -> Self {
80         ExitFailure(t.into())
81     }
82 }
83 
84 /// A newtype wrapper around `E: std::fmt::Display`
85 ///
86 /// ```rust,should_panic
87 /// # extern crate exitfailure;
88 /// # use exitfailure::ExitDisplay;
89 /// fn main() -> Result<(), ExitDisplay<String>> {
90 ///     Ok(some_fn()?)
91 /// }
92 ///
93 /// fn some_fn() -> Result<(), String> {
94 ///     Err("some error".into())
95 /// }
96 /// ```
97 pub struct ExitDisplay<E: std::fmt::Display>(E);
98 
99 /// Prints the underlying error type, using `Display` and not `Debug`.
100 impl<E: std::fmt::Display> std::fmt::Debug for ExitDisplay<E> {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result101     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
102         write!(f, "{}", self.0)
103     }
104 }
105 
106 impl<E: std::fmt::Display> From<E> for ExitDisplay<E> {
from(e: E) -> Self107     fn from(e: E) -> Self {
108         ExitDisplay(e)
109     }
110 }
111 
112 #[cfg(test)]
113 mod test {
114     use super::*;
115     use std::fmt::Write;
116 
117     #[test]
test_exitfailure()118     fn test_exitfailure() {
119         let mut buffer = String::new();
120         let error = failure::err_msg("some failure").context("some context");
121         let exitfailure: ExitFailure = error.into();
122         write!(buffer, "{:?}", exitfailure).unwrap();
123         assert!(buffer.contains("some failure"));
124         assert!(buffer.contains("some context"));
125     }
126 
127     #[test]
test_exitdisplay()128     fn test_exitdisplay() {
129         let mut buffer = String::new();
130         let error = "some error".to_string();
131         let exitdisplay: ExitDisplay<String> = error.into();
132         write!(buffer, "{:?}", exitdisplay).unwrap();
133         assert_eq!(buffer, "some error");
134     }
135 }
136