1 use crate::{ErrReport, Result};
2 use std::fmt::{self, Display};
3 
4 /// A helper trait for attaching help text to errors to be displayed after the chain of errors
5 pub trait Help<T>: private::Sealed {
6     /// Add a note to an error, to be displayed after the chain of errors.
7     ///
8     /// # Examples
9     ///
10     /// ```rust
11     /// # use std::{error::Error, fmt::{self, Display}};
12     /// # use jane_eyre::Result;
13     /// # #[derive(Debug)]
14     /// # struct FakeErr;
15     /// # impl Display for FakeErr {
16     /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17     /// #         write!(f, "FakeErr")
18     /// #     }
19     /// # }
20     /// # impl std::error::Error for FakeErr {}
21     /// # fn main() -> Result<()> {
22     /// # fn fallible_fn() -> Result<(), FakeErr> {
23     /// #       Ok(())
24     /// # }
25     /// use jane_eyre::Help as _;
26     ///
27     /// fallible_fn().note("This might have failed due to ...")?;
28     /// # Ok(())
29     /// # }
30     /// ```
note<C>(self, context: C) -> Result<T> where C: Display + Send + Sync + 'static31     fn note<C>(self, context: C) -> Result<T>
32     where
33         C: Display + Send + Sync + 'static;
34 
35     /// Add a Note to an error, to be displayed after the chain of errors, which is lazily
36     /// evaluated only in the case of an error.
37     ///
38     /// # Examples
39     ///
40     /// ```rust
41     /// # use std::{error::Error, fmt::{self, Display}};
42     /// # use jane_eyre::Result;
43     /// # #[derive(Debug)]
44     /// # struct FakeErr;
45     /// # impl Display for FakeErr {
46     /// #     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47     /// #         write!(f, "FakeErr")
48     /// #     }
49     /// # }
50     /// # impl std::error::Error for FakeErr {}
51     /// # fn main() -> Result<()> {
52     /// # fn fallible_fn() -> Result<(), FakeErr> {
53     /// #       Ok(())
54     /// # }
55     /// use jane_eyre::Help as _;
56     ///
57     /// fallible_fn().with_note(|| {
58     ///         format!("This might have failed due to ... It has failed {} times", 100)
59     ///     })?;
60     /// # Ok(())
61     /// # }
62     /// ```
with_note<C, F>(self, f: F) -> Result<T> where C: Display + Send + Sync + 'static, F: FnOnce() -> C63     fn with_note<C, F>(self, f: F) -> Result<T>
64     where
65         C: Display + Send + Sync + 'static,
66         F: FnOnce() -> C;
67 
68     /// Add a Warning to an error, to be displayed after the chain of errors.
warning<C>(self, context: C) -> Result<T> where C: Display + Send + Sync + 'static69     fn warning<C>(self, context: C) -> Result<T>
70     where
71         C: Display + Send + Sync + 'static;
72 
73     /// Add a Warning to an error, to be displayed after the chain of errors, which is lazily
74     /// evaluated only in the case of an error.
with_warning<C, F>(self, f: F) -> Result<T> where C: Display + Send + Sync + 'static, F: FnOnce() -> C75     fn with_warning<C, F>(self, f: F) -> Result<T>
76     where
77         C: Display + Send + Sync + 'static,
78         F: FnOnce() -> C;
79 
80     /// Add a Suggestion to an error, to be displayed after the chain of errors.
suggestion<C>(self, context: C) -> Result<T> where C: Display + Send + Sync + 'static81     fn suggestion<C>(self, context: C) -> Result<T>
82     where
83         C: Display + Send + Sync + 'static;
84 
85     /// Add a Suggestion to an error, to be displayed after the chain of errors, which is lazily
86     /// evaluated only in the case of an error.
with_suggestion<C, F>(self, f: F) -> Result<T> where C: Display + Send + Sync + 'static, F: FnOnce() -> C87     fn with_suggestion<C, F>(self, f: F) -> Result<T>
88     where
89         C: Display + Send + Sync + 'static,
90         F: FnOnce() -> C;
91 }
92 
93 impl<T, E> Help<T> for std::result::Result<T, E>
94 where
95     E: Into<ErrReport>,
96 {
note<C>(self, context: C) -> Result<T> where C: Display + Send + Sync + 'static,97     fn note<C>(self, context: C) -> Result<T>
98     where
99         C: Display + Send + Sync + 'static,
100     {
101         self.map_err(|e| {
102             let mut e = e.into();
103             e.context_mut().help.push(HelpInfo::Note(Box::new(context)));
104             e
105         })
106     }
107 
with_note<C, F>(self, context: F) -> Result<T> where C: Display + Send + Sync + 'static, F: FnOnce() -> C,108     fn with_note<C, F>(self, context: F) -> Result<T>
109     where
110         C: Display + Send + Sync + 'static,
111         F: FnOnce() -> C,
112     {
113         self.map_err(|e| {
114             let mut e = e.into();
115             e.context_mut()
116                 .help
117                 .push(HelpInfo::Note(Box::new(context())));
118             e
119         })
120     }
121 
warning<C>(self, context: C) -> Result<T> where C: Display + Send + Sync + 'static,122     fn warning<C>(self, context: C) -> Result<T>
123     where
124         C: Display + Send + Sync + 'static,
125     {
126         self.map_err(|e| {
127             let mut e = e.into();
128             e.context_mut()
129                 .help
130                 .push(HelpInfo::Warning(Box::new(context)));
131             e
132         })
133     }
134 
with_warning<C, F>(self, context: F) -> Result<T> where C: Display + Send + Sync + 'static, F: FnOnce() -> C,135     fn with_warning<C, F>(self, context: F) -> Result<T>
136     where
137         C: Display + Send + Sync + 'static,
138         F: FnOnce() -> C,
139     {
140         self.map_err(|e| {
141             let mut e = e.into();
142             e.context_mut()
143                 .help
144                 .push(HelpInfo::Warning(Box::new(context())));
145             e
146         })
147     }
148 
suggestion<C>(self, context: C) -> Result<T> where C: Display + Send + Sync + 'static,149     fn suggestion<C>(self, context: C) -> Result<T>
150     where
151         C: Display + Send + Sync + 'static,
152     {
153         self.map_err(|e| {
154             let mut e = e.into();
155             e.context_mut()
156                 .help
157                 .push(HelpInfo::Suggestion(Box::new(context)));
158             e
159         })
160     }
161 
with_suggestion<C, F>(self, context: F) -> Result<T> where C: Display + Send + Sync + 'static, F: FnOnce() -> C,162     fn with_suggestion<C, F>(self, context: F) -> Result<T>
163     where
164         C: Display + Send + Sync + 'static,
165         F: FnOnce() -> C,
166     {
167         self.map_err(|e| {
168             let mut e = e.into();
169             e.context_mut()
170                 .help
171                 .push(HelpInfo::Suggestion(Box::new(context())));
172             e
173         })
174     }
175 }
176 
177 pub enum HelpInfo {
178     Note(Box<dyn Display + Send + Sync + 'static>),
179     Warning(Box<dyn Display + Send + Sync + 'static>),
180     Suggestion(Box<dyn Display + Send + Sync + 'static>),
181 }
182 
183 impl Display for HelpInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result184     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185         match self {
186             Self::Note(context) => write!(f, "Note: {}", context),
187             Self::Warning(context) => write!(f, "Warning: {}", context),
188             Self::Suggestion(context) => write!(f, "Suggestion: {}", context),
189         }
190     }
191 }
192 
193 pub(crate) mod private {
194     use crate::ErrReport;
195     pub trait Sealed {}
196 
197     impl<T, E> Sealed for std::result::Result<T, E> where E: Into<ErrReport> {}
198 }
199