#![cfg_attr(doc_cfg, feature(doc_cfg))] #![allow( clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::wrong_self_convention )] use core::fmt::Display; use std::error::Error as StdError; use once_cell::sync::OnceCell; #[allow(unreachable_pub)] pub use into_diagnostic::*; #[doc(hidden)] #[allow(unreachable_pub)] pub use Report as ErrReport; /// Compatibility re-export of `Report` for interop with `anyhow` #[allow(unreachable_pub)] pub use Report as Error; #[doc(hidden)] #[allow(unreachable_pub)] pub use ReportHandler as EyreContext; /// Compatibility re-export of `WrapErr` for interop with `anyhow` #[allow(unreachable_pub)] pub use WrapErr as Context; #[cfg(not(feature = "fancy-no-backtrace"))] use crate::DebugReportHandler; use crate::Diagnostic; #[cfg(feature = "fancy-no-backtrace")] use crate::MietteHandler; use error::ErrorImpl; use self::ptr::Own; mod context; mod error; mod fmt; mod into_diagnostic; mod kind; mod macros; mod ptr; mod wrapper; /** Core Diagnostic wrapper type. ## `eyre` Users You can just replace `use`s of `eyre::Report` with `miette::Report`. */ pub struct Report { inner: Own>, } unsafe impl Sync for Report {} unsafe impl Send for Report {} /// pub type ErrorHook = Box Box + Sync + Send + 'static>; static HOOK: OnceCell = OnceCell::new(); /// Error indicating that [`set_hook()`] was unable to install the provided /// [`ErrorHook`]. #[derive(Debug)] pub struct InstallError; impl core::fmt::Display for InstallError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str("cannot install provided ErrorHook, a hook has already been installed") } } impl StdError for InstallError {} impl Diagnostic for InstallError {} /** Set the error hook. */ pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> { HOOK.set(hook).map_err(|_| InstallError) } #[cfg_attr(track_caller, track_caller)] #[cfg_attr(not(track_caller), allow(unused_mut))] fn capture_handler(error: &(dyn Diagnostic + 'static)) -> Box { let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref(); #[cfg(track_caller)] { let mut handler = hook(error); handler.track_caller(std::panic::Location::caller()); handler } #[cfg(not(track_caller))] { hook(error) } } fn get_default_printer(_err: &(dyn Diagnostic + 'static)) -> Box { #[cfg(feature = "fancy-no-backtrace")] return Box::new(MietteHandler::new()); #[cfg(not(feature = "fancy-no-backtrace"))] return Box::new(DebugReportHandler::new()); } impl dyn ReportHandler { /// pub fn is(&self) -> bool { // Get `TypeId` of the type this function is instantiated with. let t = core::any::TypeId::of::(); // Get `TypeId` of the type in the trait object (`self`). let concrete = self.type_id(); // Compare both `TypeId`s on equality. t == concrete } /// pub fn downcast_ref(&self) -> Option<&T> { if self.is::() { unsafe { Some(&*(self as *const dyn ReportHandler as *const T)) } } else { None } } /// pub fn downcast_mut(&mut self) -> Option<&mut T> { if self.is::() { unsafe { Some(&mut *(self as *mut dyn ReportHandler as *mut T)) } } else { None } } } /// Error Report Handler trait for customizing `miette::Report` pub trait ReportHandler: core::any::Any + Send + Sync { /// Define the report format /// /// Used to override the report format of `miette::Report` /// /// # Example /// /// ```rust /// use indenter::indented; /// use miette::{Diagnostic, ReportHandler}; /// /// pub struct Handler; /// /// impl ReportHandler for Handler { /// fn debug( /// &self, /// error: &dyn Diagnostic, /// f: &mut core::fmt::Formatter<'_>, /// ) -> core::fmt::Result { /// use core::fmt::Write as _; /// /// if f.alternate() { /// return core::fmt::Debug::fmt(error, f); /// } /// /// write!(f, "{}", error)?; /// /// Ok(()) /// } /// } /// ``` fn debug( &self, error: &(dyn Diagnostic), f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result; /// Override for the `Display` format fn display( &self, error: &(dyn StdError + 'static), f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result { write!(f, "{}", error)?; if f.alternate() { for cause in crate::chain::Chain::new(error).skip(1) { write!(f, ": {}", cause)?; } } Ok(()) } /// Store the location of the caller who constructed this error report #[allow(unused_variables)] fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {} } /// type alias for `Result` /// /// This is a reasonable return type to use throughout your application but also /// for `main()`. If you do, failures will be printed along with a backtrace if /// one was captured. /// /// `miette::Result` may be used with one *or* two type parameters. /// /// ```rust /// use miette::Result; /// /// # const IGNORE: &str = stringify! { /// fn demo1() -> Result {...} /// // ^ equivalent to std::result::Result /// /// fn demo2() -> Result {...} /// // ^ equivalent to std::result::Result /// # }; /// ``` /// /// # Example /// /// ``` /// # pub trait Deserialize {} /// # /// # mod serde_json { /// # use super::Deserialize; /// # use std::io; /// # /// # pub fn from_str(json: &str) -> io::Result { /// # unimplemented!() /// # } /// # } /// # /// # #[derive(Debug)] /// # struct ClusterMap; /// # /// # impl Deserialize for ClusterMap {} /// # /// use miette::{IntoDiagnostic, Result}; /// /// fn main() -> Result<()> { /// # return Ok(()); /// let config = std::fs::read_to_string("cluster.json").into_diagnostic()?; /// let map: ClusterMap = serde_json::from_str(&config).into_diagnostic()?; /// println!("cluster info: {:#?}", map); /// Ok(()) /// } /// ``` /// /// ## `anyhow`/`eyre` Users /// /// You can just replace `use`s of `anyhow::Result`/`eyre::Result` with /// `miette::Result`. pub type Result = core::result::Result; /// Provides the [`wrap_err()`](WrapErr::wrap_err) method for [`Result`]. /// /// This trait is sealed and cannot be implemented for types outside of /// `miette`. /// /// # Example /// /// ``` /// use miette::{WrapErr, IntoDiagnostic, Result}; /// use std::{fs, path::PathBuf}; /// /// pub struct ImportantThing { /// path: PathBuf, /// } /// /// impl ImportantThing { /// # const IGNORE: &'static str = stringify! { /// pub fn detach(&mut self) -> Result<()> {...} /// # }; /// # fn detach(&mut self) -> Result<()> { /// # unimplemented!() /// # } /// } /// /// pub fn do_it(mut it: ImportantThing) -> Result> { /// it.detach().wrap_err("Failed to detach the important thing")?; /// /// let path = &it.path; /// let content = fs::read(path) /// .into_diagnostic() /// .wrap_err_with(|| format!( /// "Failed to read instrs from {}", /// path.display()) /// )?; /// /// Ok(content) /// } /// ``` /// /// When printed, the outermost error would be printed first and the lower /// level underlying causes would be enumerated below. /// /// ```console /// Error: Failed to read instrs from ./path/to/instrs.json /// /// Caused by: /// No such file or directory (os error 2) /// ``` /// /// # Wrapping Types That Do Not Implement `Error` /// /// For example `&str` and `Box`. /// /// Due to restrictions for coherence `Report` cannot implement `From` for types /// that don't implement `Error`. Attempts to do so will give `"this type might /// implement Error in the future"` as an error. As such, `wrap_err()`, which /// uses `From` under the hood, cannot be used to wrap these types. Instead we /// encourage you to use the combinators provided for `Result` in `std`/`core`. /// /// For example, instead of this: /// /// ```rust,compile_fail /// use std::error::Error; /// use miette::{WrapErr, Report}; /// /// fn wrap_example(err: Result<(), Box>) /// -> Result<(), Report> /// { /// err.wrap_err("saw a downstream error") /// } /// ``` /// /// We encourage you to write this: /// /// ```rust /// use miette::{miette, Report, WrapErr}; /// use std::error::Error; /// /// fn wrap_example(err: Result<(), Box>) -> Result<(), Report> { /// err.map_err(|e| miette!(e)) /// .wrap_err("saw a downstream error") /// } /// ``` /// /// # Effect on Downcasting /// /// After attaching a message of type `D` onto an error of type `E`, the /// resulting `miette::Error` may be downcast to `D` **or** to `E`. /// /// That is, in codebases that rely on downcasting, `miette`'s `wrap_err()` /// supports both of the following use cases: /// /// - **Attaching messages whose type is insignificant onto errors whose type /// is used in downcasts.** /// /// In other error libraries whose `wrap_err()` is not designed this way, it /// can be risky to introduce messages to existing code because new message /// might break existing working downcasts. In miette, any downcast that /// worked before adding the message will continue to work after you add a /// message, so you should freely wrap errors wherever it would be helpful. /// /// ``` /// # use miette::bail; /// # use thiserror::Error; /// # /// # #[derive(Error, Debug)] /// # #[error("???")] /// # struct SuspiciousError; /// # /// # fn helper() -> Result<()> { /// # bail!(SuspiciousError); /// # } /// # /// use miette::{WrapErr, Result}; /// /// fn do_it() -> Result<()> { /// helper().wrap_err("Failed to complete the work")?; /// # const IGNORE: &str = stringify! { /// ... /// # }; /// # unreachable!() /// } /// /// fn main() { /// let err = do_it().unwrap_err(); /// if let Some(e) = err.downcast_ref::() { /// // If helper() returned SuspiciousError, this downcast will /// // correctly succeed even with the message in between. /// # return; /// } /// # panic!("expected downcast to succeed"); /// } /// ``` /// /// - **Attaching message whose type is used in downcasts onto errors whose /// type is insignificant.** /// /// Some codebases prefer to use machine-readable messages to categorize /// lower level errors in a way that will be actionable to higher levels of /// the application. /// /// ``` /// # use miette::bail; /// # use thiserror::Error; /// # /// # #[derive(Error, Debug)] /// # #[error("???")] /// # struct HelperFailed; /// # /// # fn helper() -> Result<()> { /// # bail!("no such file or directory"); /// # } /// # /// use miette::{WrapErr, Result}; /// /// fn do_it() -> Result<()> { /// helper().wrap_err(HelperFailed)?; /// # const IGNORE: &str = stringify! { /// ... /// # }; /// # unreachable!() /// } /// /// fn main() { /// let err = do_it().unwrap_err(); /// if let Some(e) = err.downcast_ref::() { /// // If helper failed, this downcast will succeed because /// // HelperFailed is the message that has been attached to /// // that error. /// # return; /// } /// # panic!("expected downcast to succeed"); /// } /// ``` pub trait WrapErr: context::private::Sealed { /// Wrap the error value with a new adhoc error #[cfg_attr(track_caller, track_caller)] fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Wrap the error value with a new adhoc error that is evaluated lazily /// only once an error does occur. #[cfg_attr(track_caller, track_caller)] fn wrap_err_with(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; /// Compatibility re-export of `wrap_err()` for interop with `anyhow` #[cfg_attr(track_caller, track_caller)] fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Compatibility re-export of `wrap_err_with()` for interop with `anyhow` #[cfg_attr(track_caller, track_caller)] fn with_context(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; } // Private API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { use super::Report; use core::fmt::{Debug, Display}; pub use core::result::Result::Err; #[doc(hidden)] pub mod kind { pub use super::super::kind::{AdhocKind, TraitKind}; pub use super::super::kind::BoxedKind; } #[cfg_attr(track_caller, track_caller)] pub fn new_adhoc(message: M) -> Report where M: Display + Debug + Send + Sync + 'static, { Report::from_adhoc(message) } }