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