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