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