1 #![cfg_attr(doc_cfg, feature(doc_cfg))]
2 #![allow(
3     clippy::needless_doctest_main,
4     clippy::new_ret_no_self,
5     clippy::wrong_self_convention
6 )]
7 use core::fmt::Display;
8 
9 use std::error::Error as StdError;
10 
11 use once_cell::sync::OnceCell;
12 
13 #[allow(unreachable_pub)]
14 pub use into_diagnostic::*;
15 #[doc(hidden)]
16 #[allow(unreachable_pub)]
17 pub use Report as ErrReport;
18 /// Compatibility re-export of `Report` for interop with `anyhow`
19 #[allow(unreachable_pub)]
20 pub use Report as Error;
21 #[doc(hidden)]
22 #[allow(unreachable_pub)]
23 pub use ReportHandler as EyreContext;
24 /// Compatibility re-export of `WrapErr` for interop with `anyhow`
25 #[allow(unreachable_pub)]
26 pub use WrapErr as Context;
27 
28 #[cfg(not(feature = "fancy-no-backtrace"))]
29 use crate::DebugReportHandler;
30 use crate::Diagnostic;
31 #[cfg(feature = "fancy-no-backtrace")]
32 use crate::MietteHandler;
33 
34 use error::ErrorImpl;
35 
36 use self::ptr::Own;
37 
38 mod context;
39 mod error;
40 mod fmt;
41 mod into_diagnostic;
42 mod kind;
43 mod macros;
44 mod ptr;
45 mod wrapper;
46 
47 /**
48 Core Diagnostic wrapper type.
49 
50 ## `eyre` Users
51 
52 You can just replace `use`s of `eyre::Report` with `miette::Report`.
53 */
54 pub struct Report {
55     inner: Own<ErrorImpl<()>>,
56 }
57 
58 unsafe impl Sync for Report {}
59 unsafe impl Send for Report {}
60 
61 ///
62 pub type ErrorHook =
63     Box<dyn Fn(&(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> + Sync + Send + 'static>;
64 
65 static HOOK: OnceCell<ErrorHook> = OnceCell::new();
66 
67 /// Error indicating that [`set_hook()`] was unable to install the provided
68 /// [`ErrorHook`].
69 #[derive(Debug)]
70 pub struct InstallError;
71 
72 impl core::fmt::Display for InstallError {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result73     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74         f.write_str("cannot install provided ErrorHook, a hook has already been installed")
75     }
76 }
77 
78 impl StdError for InstallError {}
79 impl Diagnostic for InstallError {}
80 
81 /**
82 Set the error hook.
83 */
set_hook(hook: ErrorHook) -> Result<(), InstallError>84 pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
85     HOOK.set(hook).map_err(|_| InstallError)
86 }
87 
88 #[cfg_attr(track_caller, track_caller)]
89 #[cfg_attr(not(track_caller), allow(unused_mut))]
capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler>90 fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler> {
91     let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref();
92 
93     #[cfg(track_caller)]
94     {
95         let mut handler = hook(error);
96         handler.track_caller(std::panic::Location::caller());
97         handler
98     }
99     #[cfg(not(track_caller))]
100     {
101         hook(error)
102     }
103 }
104 
get_default_printer(_err: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler + 'static>105 fn get_default_printer(_err: &(dyn Diagnostic + 'static)) -> Box<dyn ReportHandler + 'static> {
106     #[cfg(feature = "fancy-no-backtrace")]
107     return Box::new(MietteHandler::new());
108     #[cfg(not(feature = "fancy-no-backtrace"))]
109     return Box::new(DebugReportHandler::new());
110 }
111 
112 impl dyn ReportHandler {
113     ///
is<T: ReportHandler>(&self) -> bool114     pub fn is<T: ReportHandler>(&self) -> bool {
115         // Get `TypeId` of the type this function is instantiated with.
116         let t = core::any::TypeId::of::<T>();
117 
118         // Get `TypeId` of the type in the trait object (`self`).
119         let concrete = self.type_id();
120 
121         // Compare both `TypeId`s on equality.
122         t == concrete
123     }
124 
125     ///
downcast_ref<T: ReportHandler>(&self) -> Option<&T>126     pub fn downcast_ref<T: ReportHandler>(&self) -> Option<&T> {
127         if self.is::<T>() {
128             unsafe { Some(&*(self as *const dyn ReportHandler as *const T)) }
129         } else {
130             None
131         }
132     }
133 
134     ///
downcast_mut<T: ReportHandler>(&mut self) -> Option<&mut T>135     pub fn downcast_mut<T: ReportHandler>(&mut self) -> Option<&mut T> {
136         if self.is::<T>() {
137             unsafe { Some(&mut *(self as *mut dyn ReportHandler as *mut T)) }
138         } else {
139             None
140         }
141     }
142 }
143 
144 /// Error Report Handler trait for customizing `miette::Report`
145 pub trait ReportHandler: core::any::Any + Send + Sync {
146     /// Define the report format
147     ///
148     /// Used to override the report format of `miette::Report`
149     ///
150     /// # Example
151     ///
152     /// ```rust
153     /// use indenter::indented;
154     /// use miette::{Diagnostic, ReportHandler};
155     ///
156     /// pub struct Handler;
157     ///
158     /// impl ReportHandler for Handler {
159     ///     fn debug(
160     ///         &self,
161     ///         error: &dyn Diagnostic,
162     ///         f: &mut core::fmt::Formatter<'_>,
163     ///     ) -> core::fmt::Result {
164     ///         use core::fmt::Write as _;
165     ///
166     ///         if f.alternate() {
167     ///             return core::fmt::Debug::fmt(error, f);
168     ///         }
169     ///
170     ///         write!(f, "{}", error)?;
171     ///
172     ///         Ok(())
173     ///     }
174     /// }
175     /// ```
debug( &self, error: &(dyn Diagnostic), f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result176     fn debug(
177         &self,
178         error: &(dyn Diagnostic),
179         f: &mut core::fmt::Formatter<'_>,
180     ) -> core::fmt::Result;
181 
182     /// Override for the `Display` format
display( &self, error: &(dyn StdError + 'static), f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result183     fn display(
184         &self,
185         error: &(dyn StdError + 'static),
186         f: &mut core::fmt::Formatter<'_>,
187     ) -> core::fmt::Result {
188         write!(f, "{}", error)?;
189 
190         if f.alternate() {
191             for cause in crate::chain::Chain::new(error).skip(1) {
192                 write!(f, ": {}", cause)?;
193             }
194         }
195 
196         Ok(())
197     }
198 
199     /// Store the location of the caller who constructed this error report
200     #[allow(unused_variables)]
track_caller(&mut self, location: &'static std::panic::Location<'static>)201     fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {}
202 }
203 
204 /// type alias for `Result<T, Report>`
205 ///
206 /// This is a reasonable return type to use throughout your application but also
207 /// for `main()`. If you do, failures will be printed along with a backtrace if
208 /// one was captured.
209 ///
210 /// `miette::Result` may be used with one *or* two type parameters.
211 ///
212 /// ```rust
213 /// use miette::Result;
214 ///
215 /// # const IGNORE: &str = stringify! {
216 /// fn demo1() -> Result<T> {...}
217 ///            // ^ equivalent to std::result::Result<T, miette::Error>
218 ///
219 /// fn demo2() -> Result<T, OtherError> {...}
220 ///            // ^ equivalent to std::result::Result<T, OtherError>
221 /// # };
222 /// ```
223 ///
224 /// # Example
225 ///
226 /// ```
227 /// # pub trait Deserialize {}
228 /// #
229 /// # mod serde_json {
230 /// #     use super::Deserialize;
231 /// #     use std::io;
232 /// #
233 /// #     pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> {
234 /// #         unimplemented!()
235 /// #     }
236 /// # }
237 /// #
238 /// # #[derive(Debug)]
239 /// # struct ClusterMap;
240 /// #
241 /// # impl Deserialize for ClusterMap {}
242 /// #
243 /// use miette::{IntoDiagnostic, Result};
244 ///
245 /// fn main() -> Result<()> {
246 ///     # return Ok(());
247 ///     let config = std::fs::read_to_string("cluster.json").into_diagnostic()?;
248 ///     let map: ClusterMap = serde_json::from_str(&config).into_diagnostic()?;
249 ///     println!("cluster info: {:#?}", map);
250 ///     Ok(())
251 /// }
252 /// ```
253 ///
254 /// ## `anyhow`/`eyre` Users
255 ///
256 /// You can just replace `use`s of `anyhow::Result`/`eyre::Result` with
257 /// `miette::Result`.
258 pub type Result<T, E = Report> = core::result::Result<T, E>;
259 
260 /// Provides the [`wrap_err()`](WrapErr::wrap_err) method for [`Result`].
261 ///
262 /// This trait is sealed and cannot be implemented for types outside of
263 /// `miette`.
264 ///
265 /// # Example
266 ///
267 /// ```
268 /// use miette::{WrapErr, IntoDiagnostic, Result};
269 /// use std::{fs, path::PathBuf};
270 ///
271 /// pub struct ImportantThing {
272 ///     path: PathBuf,
273 /// }
274 ///
275 /// impl ImportantThing {
276 ///     # const IGNORE: &'static str = stringify! {
277 ///     pub fn detach(&mut self) -> Result<()> {...}
278 ///     # };
279 ///     # fn detach(&mut self) -> Result<()> {
280 ///     #     unimplemented!()
281 ///     # }
282 /// }
283 ///
284 /// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> {
285 ///     it.detach().wrap_err("Failed to detach the important thing")?;
286 ///
287 ///     let path = &it.path;
288 ///     let content = fs::read(path)
289 ///         .into_diagnostic()
290 ///         .wrap_err_with(|| format!(
291 ///             "Failed to read instrs from {}",
292 ///             path.display())
293 ///         )?;
294 ///
295 ///     Ok(content)
296 /// }
297 /// ```
298 ///
299 /// When printed, the outermost error would be printed first and the lower
300 /// level underlying causes would be enumerated below.
301 ///
302 /// ```console
303 /// Error: Failed to read instrs from ./path/to/instrs.json
304 ///
305 /// Caused by:
306 ///     No such file or directory (os error 2)
307 /// ```
308 ///
309 /// # Wrapping Types That Do Not Implement `Error`
310 ///
311 /// For example `&str` and `Box<dyn Error>`.
312 ///
313 /// Due to restrictions for coherence `Report` cannot implement `From` for types
314 /// that don't implement `Error`. Attempts to do so will give `"this type might
315 /// implement Error in the future"` as an error. As such, `wrap_err()`, which
316 /// uses `From` under the hood, cannot be used to wrap these types. Instead we
317 /// encourage you to use the combinators provided for `Result` in `std`/`core`.
318 ///
319 /// For example, instead of this:
320 ///
321 /// ```rust,compile_fail
322 /// use std::error::Error;
323 /// use miette::{WrapErr, Report};
324 ///
325 /// fn wrap_example(err: Result<(), Box<dyn Error + Send + Sync + 'static>>)
326 ///     -> Result<(), Report>
327 /// {
328 ///     err.wrap_err("saw a downstream error")
329 /// }
330 /// ```
331 ///
332 /// We encourage you to write this:
333 ///
334 /// ```rust
335 /// use miette::{miette, Report, WrapErr};
336 /// use std::error::Error;
337 ///
338 /// fn wrap_example(err: Result<(), Box<dyn Error + Send + Sync + 'static>>) -> Result<(), Report> {
339 ///     err.map_err(|e| miette!(e))
340 ///         .wrap_err("saw a downstream error")
341 /// }
342 /// ```
343 ///
344 /// # Effect on Downcasting
345 ///
346 /// After attaching a message of type `D` onto an error of type `E`, the
347 /// resulting `miette::Error` may be downcast to `D` **or** to `E`.
348 ///
349 /// That is, in codebases that rely on downcasting, `miette`'s `wrap_err()`
350 /// supports both of the following use cases:
351 ///
352 ///   - **Attaching messages whose type is insignificant onto errors whose type
353 ///     is used in downcasts.**
354 ///
355 ///     In other error libraries whose `wrap_err()` is not designed this way, it
356 ///     can be risky to introduce messages to existing code because new message
357 ///     might break existing working downcasts. In miette, any downcast that
358 ///     worked before adding the message will continue to work after you add a
359 ///     message, so you should freely wrap errors wherever it would be helpful.
360 ///
361 ///     ```
362 ///     # use miette::bail;
363 ///     # use thiserror::Error;
364 ///     #
365 ///     # #[derive(Error, Debug)]
366 ///     # #[error("???")]
367 ///     # struct SuspiciousError;
368 ///     #
369 ///     # fn helper() -> Result<()> {
370 ///     #     bail!(SuspiciousError);
371 ///     # }
372 ///     #
373 ///     use miette::{WrapErr, Result};
374 ///
375 ///     fn do_it() -> Result<()> {
376 ///         helper().wrap_err("Failed to complete the work")?;
377 ///         # const IGNORE: &str = stringify! {
378 ///         ...
379 ///         # };
380 ///         # unreachable!()
381 ///     }
382 ///
383 ///     fn main() {
384 ///         let err = do_it().unwrap_err();
385 ///         if let Some(e) = err.downcast_ref::<SuspiciousError>() {
386 ///             // If helper() returned SuspiciousError, this downcast will
387 ///             // correctly succeed even with the message in between.
388 ///             # return;
389 ///         }
390 ///         # panic!("expected downcast to succeed");
391 ///     }
392 ///     ```
393 ///
394 ///   - **Attaching message whose type is used in downcasts onto errors whose
395 ///     type is insignificant.**
396 ///
397 ///     Some codebases prefer to use machine-readable messages to categorize
398 ///     lower level errors in a way that will be actionable to higher levels of
399 ///     the application.
400 ///
401 ///     ```
402 ///     # use miette::bail;
403 ///     # use thiserror::Error;
404 ///     #
405 ///     # #[derive(Error, Debug)]
406 ///     # #[error("???")]
407 ///     # struct HelperFailed;
408 ///     #
409 ///     # fn helper() -> Result<()> {
410 ///     #     bail!("no such file or directory");
411 ///     # }
412 ///     #
413 ///     use miette::{WrapErr, Result};
414 ///
415 ///     fn do_it() -> Result<()> {
416 ///         helper().wrap_err(HelperFailed)?;
417 ///         # const IGNORE: &str = stringify! {
418 ///         ...
419 ///         # };
420 ///         # unreachable!()
421 ///     }
422 ///
423 ///     fn main() {
424 ///         let err = do_it().unwrap_err();
425 ///         if let Some(e) = err.downcast_ref::<HelperFailed>() {
426 ///             // If helper failed, this downcast will succeed because
427 ///             // HelperFailed is the message that has been attached to
428 ///             // that error.
429 ///             # return;
430 ///         }
431 ///         # panic!("expected downcast to succeed");
432 ///     }
433 ///     ```
434 pub trait WrapErr<T, E>: context::private::Sealed {
435     /// Wrap the error value with a new adhoc error
436     #[cfg_attr(track_caller, track_caller)]
wrap_err<D>(self, msg: D) -> Result<T, Report> where D: Display + Send + Sync + 'static437     fn wrap_err<D>(self, msg: D) -> Result<T, Report>
438     where
439         D: Display + Send + Sync + 'static;
440 
441     /// Wrap the error value with a new adhoc error that is evaluated lazily
442     /// only once an error does occur.
443     #[cfg_attr(track_caller, track_caller)]
wrap_err_with<D, F>(self, f: F) -> Result<T, Report> where D: Display + Send + Sync + 'static, F: FnOnce() -> D444     fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report>
445     where
446         D: Display + Send + Sync + 'static,
447         F: FnOnce() -> D;
448 
449     /// Compatibility re-export of `wrap_err()` for interop with `anyhow`
450     #[cfg_attr(track_caller, track_caller)]
context<D>(self, msg: D) -> Result<T, Report> where D: Display + Send + Sync + 'static451     fn context<D>(self, msg: D) -> Result<T, Report>
452     where
453         D: Display + Send + Sync + 'static;
454 
455     /// Compatibility re-export of `wrap_err_with()` for interop with `anyhow`
456     #[cfg_attr(track_caller, track_caller)]
with_context<D, F>(self, f: F) -> Result<T, Report> where D: Display + Send + Sync + 'static, F: FnOnce() -> D457     fn with_context<D, F>(self, f: F) -> Result<T, Report>
458     where
459         D: Display + Send + Sync + 'static,
460         F: FnOnce() -> D;
461 }
462 
463 // Private API. Referenced by macro-generated code.
464 #[doc(hidden)]
465 pub mod private {
466     use super::Report;
467     use core::fmt::{Debug, Display};
468 
469     pub use core::result::Result::Err;
470 
471     #[doc(hidden)]
472     pub mod kind {
473         pub use super::super::kind::{AdhocKind, TraitKind};
474 
475         pub use super::super::kind::BoxedKind;
476     }
477 
478     #[cfg_attr(track_caller, track_caller)]
new_adhoc<M>(message: M) -> Report where M: Display + Debug + Send + Sync + 'static,479     pub fn new_adhoc<M>(message: M) -> Report
480     where
481         M: Display + Debug + Send + Sync + 'static,
482     {
483         Report::from_adhoc(message)
484     }
485 }
486