color_eyre/section/
help.rs

1//! Provides an extension trait for attaching `Section` to error reports.
2use crate::{
3    config::Theme,
4    eyre::{Report, Result},
5    Section,
6};
7use indenter::indented;
8use owo_colors::OwoColorize;
9use std::fmt::Write;
10use std::fmt::{self, Display};
11
12impl Section for Report {
13    type Return = Report;
14
15    fn note<D>(mut self, note: D) -> Self::Return
16    where
17        D: Display + Send + Sync + 'static,
18    {
19        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
20            handler
21                .sections
22                .push(HelpInfo::Note(Box::new(note), handler.theme));
23        }
24
25        self
26    }
27
28    fn with_note<D, F>(mut self, note: F) -> Self::Return
29    where
30        D: Display + Send + Sync + 'static,
31        F: FnOnce() -> D,
32    {
33        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
34            handler
35                .sections
36                .push(HelpInfo::Note(Box::new(note()), handler.theme));
37        }
38
39        self
40    }
41
42    fn warning<D>(mut self, warning: D) -> Self::Return
43    where
44        D: Display + Send + Sync + 'static,
45    {
46        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
47            handler
48                .sections
49                .push(HelpInfo::Warning(Box::new(warning), handler.theme));
50        }
51
52        self
53    }
54
55    fn with_warning<D, F>(mut self, warning: F) -> Self::Return
56    where
57        D: Display + Send + Sync + 'static,
58        F: FnOnce() -> D,
59    {
60        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
61            handler
62                .sections
63                .push(HelpInfo::Warning(Box::new(warning()), handler.theme));
64        }
65
66        self
67    }
68
69    fn suggestion<D>(mut self, suggestion: D) -> Self::Return
70    where
71        D: Display + Send + Sync + 'static,
72    {
73        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
74            handler
75                .sections
76                .push(HelpInfo::Suggestion(Box::new(suggestion), handler.theme));
77        }
78
79        self
80    }
81
82    fn with_suggestion<D, F>(mut self, suggestion: F) -> Self::Return
83    where
84        D: Display + Send + Sync + 'static,
85        F: FnOnce() -> D,
86    {
87        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
88            handler
89                .sections
90                .push(HelpInfo::Suggestion(Box::new(suggestion()), handler.theme));
91        }
92
93        self
94    }
95
96    fn with_section<D, F>(mut self, section: F) -> Self::Return
97    where
98        D: Display + Send + Sync + 'static,
99        F: FnOnce() -> D,
100    {
101        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
102            let section = Box::new(section());
103            handler.sections.push(HelpInfo::Custom(section));
104        }
105
106        self
107    }
108
109    fn section<D>(mut self, section: D) -> Self::Return
110    where
111        D: Display + Send + Sync + 'static,
112    {
113        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
114            let section = Box::new(section);
115            handler.sections.push(HelpInfo::Custom(section));
116        }
117
118        self
119    }
120
121    fn error<E2>(mut self, error: E2) -> Self::Return
122    where
123        E2: std::error::Error + Send + Sync + 'static,
124    {
125        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
126            let error = error.into();
127            handler.sections.push(HelpInfo::Error(error, handler.theme));
128        }
129
130        self
131    }
132
133    fn with_error<E2, F>(mut self, error: F) -> Self::Return
134    where
135        F: FnOnce() -> E2,
136        E2: std::error::Error + Send + Sync + 'static,
137    {
138        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
139            let error = error().into();
140            handler.sections.push(HelpInfo::Error(error, handler.theme));
141        }
142
143        self
144    }
145
146    fn suppress_backtrace(mut self, suppress: bool) -> Self::Return {
147        if let Some(handler) = self.handler_mut().downcast_mut::<crate::Handler>() {
148            handler.suppress_backtrace = suppress;
149        }
150
151        self
152    }
153}
154
155impl<T, E> Section for Result<T, E>
156where
157    E: Into<Report>,
158{
159    type Return = Result<T, Report>;
160
161    fn note<D>(self, note: D) -> Self::Return
162    where
163        D: Display + Send + Sync + 'static,
164    {
165        self.map_err(|error| error.into())
166            .map_err(|report| report.note(note))
167    }
168
169    fn with_note<D, F>(self, note: F) -> Self::Return
170    where
171        D: Display + Send + Sync + 'static,
172        F: FnOnce() -> D,
173    {
174        self.map_err(|error| error.into())
175            .map_err(|report| report.note(note()))
176    }
177
178    fn warning<D>(self, warning: D) -> Self::Return
179    where
180        D: Display + Send + Sync + 'static,
181    {
182        self.map_err(|error| error.into())
183            .map_err(|report| report.warning(warning))
184    }
185
186    fn with_warning<D, F>(self, warning: F) -> Self::Return
187    where
188        D: Display + Send + Sync + 'static,
189        F: FnOnce() -> D,
190    {
191        self.map_err(|error| error.into())
192            .map_err(|report| report.warning(warning()))
193    }
194
195    fn suggestion<D>(self, suggestion: D) -> Self::Return
196    where
197        D: Display + Send + Sync + 'static,
198    {
199        self.map_err(|error| error.into())
200            .map_err(|report| report.suggestion(suggestion))
201    }
202
203    fn with_suggestion<D, F>(self, suggestion: F) -> Self::Return
204    where
205        D: Display + Send + Sync + 'static,
206        F: FnOnce() -> D,
207    {
208        self.map_err(|error| error.into())
209            .map_err(|report| report.suggestion(suggestion()))
210    }
211
212    fn with_section<D, F>(self, section: F) -> Self::Return
213    where
214        D: Display + Send + Sync + 'static,
215        F: FnOnce() -> D,
216    {
217        self.map_err(|error| error.into())
218            .map_err(|report| report.section(section()))
219    }
220
221    fn section<D>(self, section: D) -> Self::Return
222    where
223        D: Display + Send + Sync + 'static,
224    {
225        self.map_err(|error| error.into())
226            .map_err(|report| report.section(section))
227    }
228
229    fn error<E2>(self, error: E2) -> Self::Return
230    where
231        E2: std::error::Error + Send + Sync + 'static,
232    {
233        self.map_err(|error| error.into())
234            .map_err(|report| report.error(error))
235    }
236
237    fn with_error<E2, F>(self, error: F) -> Self::Return
238    where
239        F: FnOnce() -> E2,
240        E2: std::error::Error + Send + Sync + 'static,
241    {
242        self.map_err(|error| error.into())
243            .map_err(|report| report.error(error()))
244    }
245
246    fn suppress_backtrace(self, suppress: bool) -> Self::Return {
247        self.map_err(|error| error.into())
248            .map_err(|report| report.suppress_backtrace(suppress))
249    }
250}
251
252pub(crate) enum HelpInfo {
253    Error(Box<dyn std::error::Error + Send + Sync + 'static>, Theme),
254    Custom(Box<dyn Display + Send + Sync + 'static>),
255    Note(Box<dyn Display + Send + Sync + 'static>, Theme),
256    Warning(Box<dyn Display + Send + Sync + 'static>, Theme),
257    Suggestion(Box<dyn Display + Send + Sync + 'static>, Theme),
258}
259
260impl Display for HelpInfo {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        match self {
263            HelpInfo::Note(note, theme) => {
264                write!(f, "{}: {}", "Note".style(theme.help_info_note), note)
265            }
266            HelpInfo::Warning(warning, theme) => write!(
267                f,
268                "{}: {}",
269                "Warning".style(theme.help_info_warning),
270                warning
271            ),
272            HelpInfo::Suggestion(suggestion, theme) => write!(
273                f,
274                "{}: {}",
275                "Suggestion".style(theme.help_info_suggestion),
276                suggestion
277            ),
278            HelpInfo::Custom(section) => write!(f, "{}", section),
279            HelpInfo::Error(error, theme) => {
280                // a lot here
281                let errors = std::iter::successors(
282                    Some(error.as_ref() as &(dyn std::error::Error + 'static)),
283                    |e| e.source(),
284                );
285
286                write!(f, "Error:")?;
287                for (n, error) in errors.enumerate() {
288                    writeln!(f)?;
289                    write!(indented(f).ind(n), "{}", error.style(theme.help_info_error))?;
290                }
291
292                Ok(())
293            }
294        }
295    }
296}
297
298impl fmt::Debug for HelpInfo {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        match self {
301            HelpInfo::Note(note, ..) => f
302                .debug_tuple("Note")
303                .field(&format_args!("{}", note))
304                .finish(),
305            HelpInfo::Warning(warning, ..) => f
306                .debug_tuple("Warning")
307                .field(&format_args!("{}", warning))
308                .finish(),
309            HelpInfo::Suggestion(suggestion, ..) => f
310                .debug_tuple("Suggestion")
311                .field(&format_args!("{}", suggestion))
312                .finish(),
313            HelpInfo::Custom(custom, ..) => f
314                .debug_tuple("CustomSection")
315                .field(&format_args!("{}", custom))
316                .finish(),
317            HelpInfo::Error(error, ..) => f.debug_tuple("Error").field(error).finish(),
318        }
319    }
320}