| 1 | //! This library provides [`eyre::Report`][Report], a trait object based |
| 2 | //! error handling type for easy idiomatic error handling and reporting in Rust |
| 3 | //! applications. |
| 4 | //! |
| 5 | //! This crate is a fork of [`anyhow`] with support for customized |
| 6 | //! error reports. For more details on customization, check out the docs on |
| 7 | //! [`eyre::EyreHandler`]. |
| 8 | //! |
| 9 | //! ## Custom Report Handlers |
| 10 | //! |
| 11 | //! The heart of this crate is its ability to swap out the Handler type to change |
| 12 | //! what information is carried alongside errors and how the end report is |
| 13 | //! formatted. This crate is meant to be used alongside companion crates that |
| 14 | //! customize its behavior. Below is a list of known crates that export report |
| 15 | //! handlers for eyre and short summaries of what features they provide. |
| 16 | //! |
| 17 | //! - [`stable-eyre`]: Switches the backtrace type from `std`'s to `backtrace-rs`'s |
| 18 | //! so that it can be captured on stable. The report format is identical to |
| 19 | //! `DefaultHandler`'s report format. |
| 20 | //! - [`color-eyre`]: Captures a `backtrace::Backtrace` and a |
| 21 | //! `tracing_error::SpanTrace`. Provides a `Section` trait for attaching warnings |
| 22 | //! and suggestions to error reports. The end report is then pretty printed with |
| 23 | //! the help of [`color-backtrace`], [`color-spantrace`], and `ansi_term`. Check |
| 24 | //! out the README on [`color-eyre`] for details on the report format. |
| 25 | //! - [`simple-eyre`]: A minimal `EyreHandler` that captures no additional |
| 26 | //! information, for when you do not wish to capture `Backtrace`s with errors. |
| 27 | //! - [`jane-eyre`]: A report handler crate that exists purely for the pun. |
| 28 | //! Currently just re-exports `color-eyre`. |
| 29 | //! |
| 30 | //! ## Usage Recommendations and Stability Considerations |
| 31 | //! |
| 32 | //! **We recommend users do not re-export types from this library as part their |
| 33 | //! own public API for libraries with external users.** The main reason for this |
| 34 | //! is that it will make your library API break if we ever bump the major version |
| 35 | //! number on eyre and your users upgrade the eyre version they use in their |
| 36 | //! application code before you upgrade your own eyre dep version[^1]. |
| 37 | //! |
| 38 | //! However, even beyond this API stability hazard, there are other good reasons |
| 39 | //! to avoid using `eyre::Report` as your public error type. |
| 40 | //! |
| 41 | //! - You export an undocumented error interface that is otherwise still |
| 42 | //! accessible via downcast, making it hard for users to react to specific |
| 43 | //! errors while not preventing them from depending on details you didn't mean |
| 44 | //! to make part of your public API. |
| 45 | //! - This in turn makes the error types of all libraries you use a part of |
| 46 | //! your public API as well, and makes changing any of those libraries into |
| 47 | //! undetectable runtime breakage. |
| 48 | //! - If many of your errors are constructed from strings, you encourage your |
| 49 | //! users to use string comparison for reacting to specific errors, which is |
| 50 | //! brittle and turns updating error messages into potentially undetectable |
| 51 | //! runtime breakage. |
| 52 | //! |
| 53 | //! ## Details |
| 54 | //! |
| 55 | //! - Use `Result<T, eyre::Report>`, or equivalently `eyre::Result<T>`, as the |
| 56 | //! return type of any fallible function. |
| 57 | //! |
| 58 | //! Within the function, use `?` to easily propagate any error that implements the |
| 59 | //! `std::error::Error` trait. |
| 60 | //! |
| 61 | //! ```rust |
| 62 | //! # pub trait Deserialize {} |
| 63 | //! # |
| 64 | //! # mod serde_json { |
| 65 | //! # use super::Deserialize; |
| 66 | //! # use std::io; |
| 67 | //! # |
| 68 | //! # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { |
| 69 | //! # unimplemented!() |
| 70 | //! # } |
| 71 | //! # } |
| 72 | //! # |
| 73 | //! # struct ClusterMap; |
| 74 | //! # |
| 75 | //! # impl Deserialize for ClusterMap {} |
| 76 | //! # |
| 77 | //! use eyre::Result; |
| 78 | //! |
| 79 | //! fn get_cluster_info() -> Result<ClusterMap> { |
| 80 | //! let config = std::fs::read_to_string("cluster.json" )?; |
| 81 | //! let map: ClusterMap = serde_json::from_str(&config)?; |
| 82 | //! Ok(map) |
| 83 | //! } |
| 84 | //! # |
| 85 | //! # fn main() {} |
| 86 | //! ``` |
| 87 | //! |
| 88 | //! - Wrap a lower level error with a new error created from a message to help the |
| 89 | //! person troubleshooting understand the chain of failures that occurred. A |
| 90 | //! low-level error like "No such file or directory" can be annoying to debug |
| 91 | //! without more information about what higher level step the application was in |
| 92 | //! the middle of. |
| 93 | //! |
| 94 | //! ```rust |
| 95 | //! # struct It; |
| 96 | //! # |
| 97 | //! # impl It { |
| 98 | //! # fn detach(&self) -> Result<()> { |
| 99 | //! # unimplemented!() |
| 100 | //! # } |
| 101 | //! # } |
| 102 | //! # |
| 103 | //! use eyre::{WrapErr, Result}; |
| 104 | //! |
| 105 | //! fn main() -> Result<()> { |
| 106 | //! # return Ok(()); |
| 107 | //! # |
| 108 | //! # const _: &str = stringify! { |
| 109 | //! ... |
| 110 | //! # }; |
| 111 | //! # |
| 112 | //! # let it = It; |
| 113 | //! # let path = "./path/to/instrs.json" ; |
| 114 | //! # |
| 115 | //! it.detach().wrap_err("Failed to detach the important thing" )?; |
| 116 | //! |
| 117 | //! let content = std::fs::read(path) |
| 118 | //! .wrap_err_with(|| format!("Failed to read instrs from {}" , path))?; |
| 119 | //! # |
| 120 | //! # const _: &str = stringify! { |
| 121 | //! ... |
| 122 | //! # }; |
| 123 | //! # |
| 124 | //! # Ok(()) |
| 125 | //! } |
| 126 | //! ``` |
| 127 | //! |
| 128 | //! ```console |
| 129 | //! Error: Failed to read instrs from ./path/to/instrs.json |
| 130 | //! |
| 131 | //! Caused by: |
| 132 | //! No such file or directory (os error 2) |
| 133 | //! ``` |
| 134 | //! |
| 135 | //! - Downcasting is supported and can be done by value, by shared reference, or by |
| 136 | //! mutable reference as needed. |
| 137 | //! |
| 138 | //! ```rust |
| 139 | //! # use eyre::{Report, eyre}; |
| 140 | //! # use std::fmt::{self, Display}; |
| 141 | //! # use std::task::Poll; |
| 142 | //! # |
| 143 | //! # #[derive(Debug)] |
| 144 | //! # enum DataStoreError { |
| 145 | //! # Censored(()), |
| 146 | //! # } |
| 147 | //! # |
| 148 | //! # impl Display for DataStoreError { |
| 149 | //! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| 150 | //! # unimplemented!() |
| 151 | //! # } |
| 152 | //! # } |
| 153 | //! # |
| 154 | //! # impl std::error::Error for DataStoreError {} |
| 155 | //! # |
| 156 | //! # const REDACTED_CONTENT: () = (); |
| 157 | //! # |
| 158 | //! # #[cfg (not(feature = "auto-install" ))] |
| 159 | //! # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); |
| 160 | //! # |
| 161 | //! # let error: Report = eyre!("..." ); |
| 162 | //! # let root_cause = &error; |
| 163 | //! # |
| 164 | //! # let ret = |
| 165 | //! // If the error was caused by redaction, then return a |
| 166 | //! // tombstone instead of the content. |
| 167 | //! match root_cause.downcast_ref::<DataStoreError>() { |
| 168 | //! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), |
| 169 | //! None => Err(error), |
| 170 | //! } |
| 171 | //! # ; |
| 172 | //! ``` |
| 173 | //! |
| 174 | //! - If using the nightly channel, a backtrace is captured and printed with the |
| 175 | //! error if the underlying error type does not already provide its own. In order |
| 176 | //! to see backtraces, they must be enabled through the environment variables |
| 177 | //! described in [`std::backtrace`]: |
| 178 | //! |
| 179 | //! - If you want panics and errors to both have backtraces, set |
| 180 | //! `RUST_BACKTRACE=1`; |
| 181 | //! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; |
| 182 | //! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and |
| 183 | //! `RUST_LIB_BACKTRACE=0`. |
| 184 | //! |
| 185 | //! The tracking issue for this feature is [rust-lang/rust#53487]. |
| 186 | //! |
| 187 | //! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables |
| 188 | //! [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487 |
| 189 | //! |
| 190 | //! - Eyre works with any error type that has an impl of `std::error::Error`, |
| 191 | //! including ones defined in your crate. We do not bundle a `derive(Error)` macro |
| 192 | //! but you can write the impls yourself or use a standalone macro like |
| 193 | //! [thiserror]. |
| 194 | //! |
| 195 | //! ```rust |
| 196 | //! use thiserror::Error; |
| 197 | //! |
| 198 | //! #[derive(Error, Debug)] |
| 199 | //! pub enum FormatError { |
| 200 | //! #[error("Invalid header (expected {expected:?}, got {found:?})" )] |
| 201 | //! InvalidHeader { |
| 202 | //! expected: String, |
| 203 | //! found: String, |
| 204 | //! }, |
| 205 | //! #[error("Missing attribute: {0}" )] |
| 206 | //! MissingAttribute(String), |
| 207 | //! } |
| 208 | //! ``` |
| 209 | //! |
| 210 | //! - One-off error messages can be constructed using the `eyre!` macro, which |
| 211 | //! supports string interpolation and produces an `eyre::Report`. |
| 212 | //! |
| 213 | //! ```rust |
| 214 | //! # use eyre::{eyre, Result}; |
| 215 | //! # |
| 216 | //! # fn demo() -> Result<()> { |
| 217 | //! # let missing = "..." ; |
| 218 | //! return Err(eyre!("Missing attribute: {}" , missing)); |
| 219 | //! # Ok(()) |
| 220 | //! # } |
| 221 | //! ``` |
| 222 | //! |
| 223 | //! - On newer versions of the compiler (i.e. 1.58 and later) this macro also |
| 224 | //! supports format args captures. |
| 225 | //! |
| 226 | //! ```rust |
| 227 | //! # use eyre::{eyre, Result}; |
| 228 | //! # |
| 229 | //! # fn demo() -> Result<()> { |
| 230 | //! # let missing = "..." ; |
| 231 | //! # #[cfg (not(eyre_no_fmt_args_capture))] |
| 232 | //! return Err(eyre!("Missing attribute: {missing}" )); |
| 233 | //! # Ok(()) |
| 234 | //! # } |
| 235 | //! ``` |
| 236 | //! |
| 237 | //! ## No-std support |
| 238 | //! |
| 239 | //! No-std support was removed in 2020 in [commit 608a16a] due to unaddressed upstream breakages. |
| 240 | //! |
| 241 | //! [commit 608a16a]: https://github.com/eyre-rs/eyre/pull/29/commits/608a16aa2c2c27eca6c88001cc94c6973c18f1d5 |
| 242 | //! |
| 243 | //! ## Comparison to failure |
| 244 | //! |
| 245 | //! The `eyre::Report` type works something like `failure::Error`, but unlike |
| 246 | //! failure ours is built around the standard library's `std::error::Error` trait |
| 247 | //! rather than a separate trait `failure::Fail`. The standard library has adopted |
| 248 | //! the necessary improvements for this to be possible as part of [RFC 2504]. |
| 249 | //! |
| 250 | //! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md |
| 251 | //! |
| 252 | //! ## Comparison to thiserror |
| 253 | //! |
| 254 | //! Use `eyre` if you don't think you'll do anything with an error other than |
| 255 | //! report it. This is common in application code. Use `thiserror` if you think |
| 256 | //! you need an error type that can be handled via match or reported. This is |
| 257 | //! common in library crates where you don't know how your users will handle |
| 258 | //! your errors. |
| 259 | //! |
| 260 | //! [thiserror]: https://github.com/dtolnay/thiserror |
| 261 | //! |
| 262 | //! ## Compatibility with `anyhow` |
| 263 | //! |
| 264 | //! This crate does its best to be usable as a drop in replacement of `anyhow` and |
| 265 | //! vice-versa by re-exporting all of the renamed APIs with the names used in |
| 266 | //! `anyhow`, though there are some differences still. |
| 267 | //! |
| 268 | //! #### `Context` and `Option` |
| 269 | //! |
| 270 | //! As part of renaming `Context` to `WrapErr` we also intentionally do not |
| 271 | //! implement `WrapErr` for `Option`. This decision was made because `wrap_err` |
| 272 | //! implies that you're creating a new error that saves the old error as its |
| 273 | //! `source`. With `Option` there is no source error to wrap, so `wrap_err` ends up |
| 274 | //! being somewhat meaningless. |
| 275 | //! |
| 276 | //! Instead `eyre` offers [`OptionExt::ok_or_eyre`] to yield _static_ errors from `None`, |
| 277 | //! and intends for users to use the combinator functions provided by |
| 278 | //! `std`, converting `Option`s to `Result`s, for _dynamic_ errors. |
| 279 | //! So where you would write this with |
| 280 | //! anyhow: |
| 281 | //! |
| 282 | //! ```rust |
| 283 | //! use anyhow::Context; |
| 284 | //! |
| 285 | //! let opt: Option<()> = None; |
| 286 | //! let result_static = opt.context("static error message" ); |
| 287 | //! let result_dynamic = opt.with_context(|| format!("{} error message" , "dynamic" )); |
| 288 | //! ``` |
| 289 | //! |
| 290 | //! With `eyre` we want users to write: |
| 291 | //! |
| 292 | //! ```rust |
| 293 | //! use eyre::{eyre, OptionExt, Result}; |
| 294 | //! |
| 295 | //! # #[cfg (not(feature = "auto-install" ))] |
| 296 | //! # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); |
| 297 | //! # |
| 298 | //! let opt: Option<()> = None; |
| 299 | //! let result_static: Result<()> = opt.ok_or_eyre("static error message" ); |
| 300 | //! let result_dynamic: Result<()> = opt.ok_or_else(|| eyre!("{} error message" , "dynamic" )); |
| 301 | //! ``` |
| 302 | //! |
| 303 | //! **NOTE**: However, to help with porting we do provide a `ContextCompat` trait which |
| 304 | //! implements `context` for options which you can import to make existing |
| 305 | //! `.context` calls compile. |
| 306 | //! |
| 307 | //! [^1]: example and explanation of breakage <https://github.com/eyre-rs/eyre/issues/30#issuecomment-647650361> |
| 308 | //! |
| 309 | //! [Report]: https://docs.rs/eyre/*/eyre/struct.Report.html |
| 310 | //! [`eyre::EyreHandler`]: https://docs.rs/eyre/*/eyre/trait.EyreHandler.html |
| 311 | //! [`eyre::WrapErr`]: https://docs.rs/eyre/*/eyre/trait.WrapErr.html |
| 312 | //! [`anyhow::Context`]: https://docs.rs/anyhow/*/anyhow/trait.Context.html |
| 313 | //! [`anyhow`]: https://github.com/dtolnay/anyhow |
| 314 | //! [`tracing_error::SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html |
| 315 | //! [`stable-eyre`]: https://github.com/eyre-rs/stable-eyre |
| 316 | //! [`color-eyre`]: https://github.com/eyre-rs/color-eyre |
| 317 | //! [`jane-eyre`]: https://github.com/yaahc/jane-eyre |
| 318 | //! [`simple-eyre`]: https://github.com/eyre-rs/simple-eyre |
| 319 | //! [`color-spantrace`]: https://github.com/eyre-rs/color-spantrace |
| 320 | //! [`color-backtrace`]: https://github.com/athre0z/color-backtrace |
| 321 | #![doc (html_root_url = "https://docs.rs/eyre/0.6.12" )] |
| 322 | #![cfg_attr ( |
| 323 | nightly, |
| 324 | feature(rustdoc_missing_doc_code_examples), |
| 325 | warn(rustdoc::missing_doc_code_examples) |
| 326 | )] |
| 327 | #![warn ( |
| 328 | missing_debug_implementations, |
| 329 | missing_docs, |
| 330 | unsafe_op_in_unsafe_fn, |
| 331 | rust_2018_idioms, |
| 332 | unreachable_pub, |
| 333 | bad_style, |
| 334 | dead_code, |
| 335 | improper_ctypes, |
| 336 | non_shorthand_field_patterns, |
| 337 | no_mangle_generic_items, |
| 338 | overflowing_literals, |
| 339 | path_statements, |
| 340 | patterns_in_fns_without_body, |
| 341 | unconditional_recursion, |
| 342 | unused, |
| 343 | unused_allocation, |
| 344 | unused_comparisons, |
| 345 | unused_parens, |
| 346 | while_true |
| 347 | )] |
| 348 | #![cfg_attr (backtrace, feature(backtrace))] |
| 349 | #![cfg_attr (doc_cfg, feature(doc_cfg))] |
| 350 | #![allow ( |
| 351 | clippy::needless_doctest_main, |
| 352 | clippy::new_ret_no_self, |
| 353 | clippy::wrong_self_convention |
| 354 | )] |
| 355 | |
| 356 | extern crate alloc; |
| 357 | |
| 358 | #[macro_use ] |
| 359 | mod backtrace; |
| 360 | mod chain; |
| 361 | mod context; |
| 362 | mod error; |
| 363 | mod fmt; |
| 364 | mod kind; |
| 365 | mod macros; |
| 366 | mod option; |
| 367 | mod ptr; |
| 368 | mod wrapper; |
| 369 | |
| 370 | use crate::backtrace::Backtrace; |
| 371 | use crate::error::ErrorImpl; |
| 372 | use core::fmt::{Debug, Display}; |
| 373 | |
| 374 | use std::error::Error as StdError; |
| 375 | |
| 376 | pub use eyre as format_err; |
| 377 | /// Compatibility re-export of `eyre` for interop with `anyhow` |
| 378 | pub use eyre as anyhow; |
| 379 | use once_cell::sync::OnceCell; |
| 380 | use ptr::OwnedPtr; |
| 381 | #[doc (hidden)] |
| 382 | pub use DefaultHandler as DefaultContext; |
| 383 | #[doc (hidden)] |
| 384 | pub use EyreHandler as EyreContext; |
| 385 | #[doc (hidden)] |
| 386 | pub use Report as ErrReport; |
| 387 | /// Compatibility re-export of `Report` for interop with `anyhow` |
| 388 | pub use Report as Error; |
| 389 | /// Compatibility re-export of `WrapErr` for interop with `anyhow` |
| 390 | pub use WrapErr as Context; |
| 391 | |
| 392 | /// The core error reporting type of the library, a wrapper around a dynamic error reporting type. |
| 393 | /// |
| 394 | /// `Report` works a lot like `Box<dyn std::error::Error>`, but with these |
| 395 | /// differences: |
| 396 | /// |
| 397 | /// - `Report` requires that the error is `Send`, `Sync`, and `'static`. |
| 398 | /// - `Report` guarantees that a backtrace is available, even if the underlying |
| 399 | /// error type does not provide one. |
| 400 | /// - `Report` is represented as a narrow pointer — exactly one word in |
| 401 | /// size instead of two. |
| 402 | /// |
| 403 | /// # Display representations |
| 404 | /// |
| 405 | /// When you print an error object using "{}" or to_string(), only the outermost underlying error |
| 406 | /// is printed, not any of the lower level causes. This is exactly as if you had called the Display |
| 407 | /// impl of the error from which you constructed your eyre::Report. |
| 408 | /// |
| 409 | /// ```console |
| 410 | /// Failed to read instrs from ./path/to/instrs.json |
| 411 | /// ``` |
| 412 | /// |
| 413 | /// To print causes as well using eyre's default formatting of causes, use the |
| 414 | /// alternate selector "{:#}". |
| 415 | /// |
| 416 | /// ```console |
| 417 | /// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2) |
| 418 | /// ``` |
| 419 | /// |
| 420 | /// The Debug format "{:?}" includes your backtrace if one was captured. Note |
| 421 | /// that this is the representation you get by default if you return an error |
| 422 | /// from `fn main` instead of printing it explicitly yourself. |
| 423 | /// |
| 424 | /// ```console |
| 425 | /// Error: Failed to read instrs from ./path/to/instrs.json |
| 426 | /// |
| 427 | /// Caused by: |
| 428 | /// No such file or directory (os error 2) |
| 429 | /// |
| 430 | /// Stack backtrace: |
| 431 | /// 0: <E as eyre::context::ext::StdError>::ext_report |
| 432 | /// at /git/eyre/src/backtrace.rs:26 |
| 433 | /// 1: core::result::Result<T,E>::map_err |
| 434 | /// at /git/rustc/src/libcore/result.rs:596 |
| 435 | /// 2: eyre::context::<impl eyre::WrapErr<T,E,H> for core::result::Result<T,E>>::wrap_err_with |
| 436 | /// at /git/eyre/src/context.rs:58 |
| 437 | /// 3: testing::main |
| 438 | /// at src/main.rs:5 |
| 439 | /// 4: std::rt::lang_start |
| 440 | /// at /git/rustc/src/libstd/rt.rs:61 |
| 441 | /// 5: main |
| 442 | /// 6: __libc_start_main |
| 443 | /// 7: _start |
| 444 | /// ``` |
| 445 | /// |
| 446 | /// To see a conventional struct-style Debug representation, use "{:#?}". |
| 447 | /// |
| 448 | /// ```console |
| 449 | /// Error { |
| 450 | /// msg: "Failed to read instrs from ./path/to/instrs.json", |
| 451 | /// source: Os { |
| 452 | /// code: 2, |
| 453 | /// kind: NotFound, |
| 454 | /// message: "No such file or directory", |
| 455 | /// }, |
| 456 | /// } |
| 457 | /// ``` |
| 458 | /// |
| 459 | /// If none of the built-in representations are appropriate and you would prefer |
| 460 | /// to render the error and its cause chain yourself, it can be done by defining |
| 461 | /// your own [`EyreHandler`] and [`hook`] to use it. |
| 462 | /// |
| 463 | /// [`EyreHandler`]: trait.EyreHandler.html |
| 464 | /// [`hook`]: fn.set_hook.html |
| 465 | #[must_use ] |
| 466 | pub struct Report { |
| 467 | inner: OwnedPtr<ErrorImpl<()>>, |
| 468 | } |
| 469 | |
| 470 | type ErrorHook = |
| 471 | Box<dyn Fn(&(dyn StdError + 'static)) -> Box<dyn EyreHandler> + Sync + Send + 'static>; |
| 472 | |
| 473 | static HOOK: OnceCell<ErrorHook> = OnceCell::new(); |
| 474 | |
| 475 | /// Error indicating that `set_hook` was unable to install the provided ErrorHook |
| 476 | #[derive (Debug, Clone, Copy)] |
| 477 | pub struct InstallError; |
| 478 | |
| 479 | impl core::fmt::Display for InstallError { |
| 480 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 481 | f.write_str(data:"cannot install provided ErrorHook, a hook has already been installed" ) |
| 482 | } |
| 483 | } |
| 484 | |
| 485 | impl StdError for InstallError {} |
| 486 | |
| 487 | /// Install the provided error hook for constructing EyreHandlers when converting |
| 488 | /// Errors to Reports |
| 489 | /// |
| 490 | /// # Details |
| 491 | /// |
| 492 | /// To customize the format and content of error reports from `eyre` you must |
| 493 | /// first define a new `EyreHandler` type to capture and store the extra context |
| 494 | /// and to define the format of how to display the chain of errors and this |
| 495 | /// stored context. Once this type has been defined you must also define a global |
| 496 | /// hook used to construct these handlers whenever `Report`s are constructed. |
| 497 | /// |
| 498 | /// # Examples |
| 499 | /// |
| 500 | /// ```rust,should_panic |
| 501 | /// use backtrace::Backtrace; |
| 502 | /// use eyre::EyreHandler; |
| 503 | /// use std::error::Error; |
| 504 | /// use std::{fmt, iter}; |
| 505 | /// |
| 506 | /// fn main() -> eyre::Result<()> { |
| 507 | /// // Install our custom eyre report hook for constructing our custom Handlers |
| 508 | /// install().unwrap(); |
| 509 | /// |
| 510 | /// // construct a report with, hopefully, our custom handler! |
| 511 | /// let mut report = eyre::eyre!("hello from custom error town!" ); |
| 512 | /// |
| 513 | /// // manually set the custom msg for this report after it has been constructed |
| 514 | /// if let Some(handler) = report.handler_mut().downcast_mut::<Handler>() { |
| 515 | /// handler.custom_msg = Some("you're the best users, you know that right???" ); |
| 516 | /// } |
| 517 | /// |
| 518 | /// // print that shit!! |
| 519 | /// Err(report) |
| 520 | /// } |
| 521 | /// |
| 522 | /// // define a handler that captures backtraces unless told not to |
| 523 | /// fn install() -> Result<(), impl Error> { |
| 524 | /// let capture_backtrace = std::env::var("RUST_BACKWARDS_TRACE" ) |
| 525 | /// .map(|val| val != "0" ) |
| 526 | /// .unwrap_or(true); |
| 527 | /// |
| 528 | /// let hook = Hook { capture_backtrace }; |
| 529 | /// |
| 530 | /// eyre::set_hook(Box::new(move |e| Box::new(hook.make_handler(e)))) |
| 531 | /// } |
| 532 | /// |
| 533 | /// struct Hook { |
| 534 | /// capture_backtrace: bool, |
| 535 | /// } |
| 536 | /// |
| 537 | /// impl Hook { |
| 538 | /// fn make_handler(&self, _error: &(dyn Error + 'static)) -> Handler { |
| 539 | /// let backtrace = if self.capture_backtrace { |
| 540 | /// Some(Backtrace::new()) |
| 541 | /// } else { |
| 542 | /// None |
| 543 | /// }; |
| 544 | /// |
| 545 | /// Handler { |
| 546 | /// backtrace, |
| 547 | /// custom_msg: None, |
| 548 | /// } |
| 549 | /// } |
| 550 | /// } |
| 551 | /// |
| 552 | /// struct Handler { |
| 553 | /// // custom configured backtrace capture |
| 554 | /// backtrace: Option<Backtrace>, |
| 555 | /// // customizable message payload associated with reports |
| 556 | /// custom_msg: Option<&'static str>, |
| 557 | /// } |
| 558 | /// |
| 559 | /// impl EyreHandler for Handler { |
| 560 | /// fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 561 | /// if f.alternate() { |
| 562 | /// return fmt::Debug::fmt(error, f); |
| 563 | /// } |
| 564 | /// |
| 565 | /// let errors = iter::successors(Some(error), |error| (*error).source()); |
| 566 | /// |
| 567 | /// for (ind, error) in errors.enumerate() { |
| 568 | /// write!(f, " \n{:>4}: {}" , ind, error)?; |
| 569 | /// } |
| 570 | /// |
| 571 | /// if let Some(backtrace) = self.backtrace.as_ref() { |
| 572 | /// writeln!(f, " \n\nBacktrace: \n{:?}" , backtrace)?; |
| 573 | /// } |
| 574 | /// |
| 575 | /// if let Some(msg) = self.custom_msg.as_ref() { |
| 576 | /// writeln!(f, " \n\n{}" , msg)?; |
| 577 | /// } |
| 578 | /// |
| 579 | /// Ok(()) |
| 580 | /// } |
| 581 | /// } |
| 582 | /// ``` |
| 583 | pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> { |
| 584 | HOOK.set(hook).map_err(|_| InstallError) |
| 585 | } |
| 586 | |
| 587 | #[cfg_attr (track_caller, track_caller)] |
| 588 | #[cfg_attr (not(track_caller), allow(unused_mut))] |
| 589 | fn capture_handler(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> { |
| 590 | #[cfg (not(feature = "auto-install" ))] |
| 591 | let hook = HOOK |
| 592 | .get() |
| 593 | .expect("a handler must always be installed if the `auto-install` feature is disabled" ) |
| 594 | .as_ref(); |
| 595 | |
| 596 | #[cfg (feature = "auto-install" )] |
| 597 | let hook: &(dyn Fn(&dyn Error) -> Box<…> + Send + Sync + 'static) = HOOK&Box … + Send + Sync + 'static> |
| 598 | .get_or_init(|| Box::new(DefaultHandler::default_with)) |
| 599 | .as_ref(); |
| 600 | |
| 601 | let mut handler: Box = hook(error); |
| 602 | |
| 603 | #[cfg (track_caller)] |
| 604 | { |
| 605 | handler.track_caller(std::panic::Location::caller()) |
| 606 | } |
| 607 | |
| 608 | handler |
| 609 | } |
| 610 | |
| 611 | impl dyn EyreHandler { |
| 612 | /// |
| 613 | pub fn is<T: EyreHandler>(&self) -> bool { |
| 614 | // Get `TypeId` of the type this function is instantiated with. |
| 615 | let t = core::any::TypeId::of::<T>(); |
| 616 | |
| 617 | // Get `TypeId` of the type in the trait object (`self`). |
| 618 | let concrete = self.type_id(); |
| 619 | |
| 620 | // Compare both `TypeId`s on equality. |
| 621 | t == concrete |
| 622 | } |
| 623 | |
| 624 | /// |
| 625 | pub fn downcast_ref<T: EyreHandler>(&self) -> Option<&T> { |
| 626 | if self.is::<T>() { |
| 627 | unsafe { Some(&*(self as *const dyn EyreHandler as *const T)) } |
| 628 | } else { |
| 629 | None |
| 630 | } |
| 631 | } |
| 632 | |
| 633 | /// |
| 634 | pub fn downcast_mut<T: EyreHandler>(&mut self) -> Option<&mut T> { |
| 635 | if self.is::<T>() { |
| 636 | unsafe { Some(&mut *(self as *mut dyn EyreHandler as *mut T)) } |
| 637 | } else { |
| 638 | None |
| 639 | } |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | /// Error Report Handler trait for customizing `eyre::Report` |
| 644 | pub trait EyreHandler: core::any::Any + Send + Sync { |
| 645 | /// Define the report format |
| 646 | /// |
| 647 | /// Used to override the report format of `eyre::Report` |
| 648 | /// |
| 649 | /// # Example |
| 650 | /// |
| 651 | /// ```rust |
| 652 | /// use backtrace::Backtrace; |
| 653 | /// use eyre::EyreHandler; |
| 654 | /// use eyre::Chain; |
| 655 | /// use std::error::Error; |
| 656 | /// use indenter::indented; |
| 657 | /// |
| 658 | /// pub struct Handler { |
| 659 | /// backtrace: Backtrace, |
| 660 | /// } |
| 661 | /// |
| 662 | /// impl EyreHandler for Handler { |
| 663 | /// fn debug( |
| 664 | /// &self, |
| 665 | /// error: &(dyn Error + 'static), |
| 666 | /// f: &mut core::fmt::Formatter<'_>, |
| 667 | /// ) -> core::fmt::Result { |
| 668 | /// use core::fmt::Write as _; |
| 669 | /// |
| 670 | /// if f.alternate() { |
| 671 | /// return core::fmt::Debug::fmt(error, f); |
| 672 | /// } |
| 673 | /// |
| 674 | /// write!(f, "{}" , error)?; |
| 675 | /// |
| 676 | /// if let Some(cause) = error.source() { |
| 677 | /// write!(f, " \n\nCaused by:" )?; |
| 678 | /// let multiple = cause.source().is_some(); |
| 679 | /// |
| 680 | /// for (n, error) in Chain::new(cause).enumerate() { |
| 681 | /// writeln!(f)?; |
| 682 | /// if multiple { |
| 683 | /// write!(indented(f).ind(n), "{}" , error)?; |
| 684 | /// } else { |
| 685 | /// write!(indented(f), "{}" , error)?; |
| 686 | /// } |
| 687 | /// } |
| 688 | /// } |
| 689 | /// |
| 690 | /// let backtrace = &self.backtrace; |
| 691 | /// write!(f, " \n\nStack backtrace: \n{:?}" , backtrace)?; |
| 692 | /// |
| 693 | /// Ok(()) |
| 694 | /// } |
| 695 | /// } |
| 696 | /// ``` |
| 697 | fn debug( |
| 698 | &self, |
| 699 | error: &(dyn StdError + 'static), |
| 700 | f: &mut core::fmt::Formatter<'_>, |
| 701 | ) -> core::fmt::Result; |
| 702 | |
| 703 | /// Override for the `Display` format |
| 704 | fn display( |
| 705 | &self, |
| 706 | error: &(dyn StdError + 'static), |
| 707 | f: &mut core::fmt::Formatter<'_>, |
| 708 | ) -> core::fmt::Result { |
| 709 | write!(f, " {}" , error)?; |
| 710 | |
| 711 | if f.alternate() { |
| 712 | for cause in crate::chain::Chain::new(error).skip(1) { |
| 713 | write!(f, ": {}" , cause)?; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | Result::Ok(()) |
| 718 | } |
| 719 | |
| 720 | /// Store the location of the caller who constructed this error report |
| 721 | #[allow (unused_variables)] |
| 722 | fn track_caller(&mut self, location: &'static std::panic::Location<'static>) {} |
| 723 | } |
| 724 | |
| 725 | /// The default provided error report handler for `eyre::Report`. |
| 726 | /// |
| 727 | /// On nightly this supports conditionally capturing a `std::backtrace::Backtrace` if the source |
| 728 | /// error did not already capture one. |
| 729 | #[allow (dead_code)] |
| 730 | pub struct DefaultHandler { |
| 731 | backtrace: Option<Backtrace>, |
| 732 | #[cfg (track_caller)] |
| 733 | location: Option<&'static std::panic::Location<'static>>, |
| 734 | } |
| 735 | |
| 736 | impl DefaultHandler { |
| 737 | /// Manual hook which constructs `DefaultHandler`s. |
| 738 | /// |
| 739 | /// # Details |
| 740 | /// |
| 741 | /// When supplied to the `set_hook` function, `default_with` will cause `eyre::Report` to use |
| 742 | /// `DefaultHandler` as the error report handler. |
| 743 | /// |
| 744 | /// If the `auto-install` feature is enabled, and a user-provided hook for constructing |
| 745 | /// `EyreHandlers` was not installed using `set_hook`, `DefaultHandler::default_with` |
| 746 | /// is automatically installed as the hook. |
| 747 | /// |
| 748 | /// # Example |
| 749 | /// |
| 750 | /// ```rust,should_panic |
| 751 | /// use eyre::{DefaultHandler, eyre, InstallError, Result, set_hook}; |
| 752 | /// |
| 753 | /// fn main() -> Result<()> { |
| 754 | /// install_default().expect("default handler inexplicably already installed" ); |
| 755 | /// Err(eyre!("hello from default error city!" )) |
| 756 | /// } |
| 757 | /// |
| 758 | /// fn install_default() -> Result<(), InstallError> { |
| 759 | /// set_hook(Box::new(DefaultHandler::default_with)) |
| 760 | /// } |
| 761 | /// |
| 762 | /// ``` |
| 763 | #[allow (unused_variables)] |
| 764 | #[cfg_attr (not(feature = "auto-install" ), allow(dead_code))] |
| 765 | pub fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> { |
| 766 | let backtrace = backtrace_if_absent!(error); |
| 767 | |
| 768 | Box::new(Self { |
| 769 | backtrace, |
| 770 | #[cfg (track_caller)] |
| 771 | location: None, |
| 772 | }) |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | impl core::fmt::Debug for DefaultHandler { |
| 777 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 778 | f&mut DebugStruct<'_, '_>.debug_struct("DefaultHandler" ) |
| 779 | .field( |
| 780 | name:"backtrace" , |
| 781 | value:match &self.backtrace { |
| 782 | Some(_) => &"Some(Backtrace { ... })" , |
| 783 | None => &"None" , |
| 784 | }, |
| 785 | ) |
| 786 | .finish() |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | impl EyreHandler for DefaultHandler { |
| 791 | fn debug( |
| 792 | &self, |
| 793 | error: &(dyn StdError + 'static), |
| 794 | f: &mut core::fmt::Formatter<'_>, |
| 795 | ) -> core::fmt::Result { |
| 796 | use core::fmt::Write as _; |
| 797 | |
| 798 | if f.alternate() { |
| 799 | return core::fmt::Debug::fmt(error, f); |
| 800 | } |
| 801 | |
| 802 | write!(f, " {}" , error)?; |
| 803 | |
| 804 | if let Some(cause) = error.source() { |
| 805 | write!(f, " \n\nCaused by:" )?; |
| 806 | let multiple = cause.source().is_some(); |
| 807 | for (n, error) in crate::chain::Chain::new(cause).enumerate() { |
| 808 | writeln!(f)?; |
| 809 | if multiple { |
| 810 | write!(indenter::indented(f).ind(n), " {}" , error)?; |
| 811 | } else { |
| 812 | write!(indenter::indented(f), " {}" , error)?; |
| 813 | } |
| 814 | } |
| 815 | } |
| 816 | |
| 817 | #[cfg (all(track_caller, feature = "track-caller" ))] |
| 818 | { |
| 819 | if let Some(location) = self.location { |
| 820 | write!(f, " \n\nLocation: \n" )?; |
| 821 | write!(indenter::indented(f), "{}" , location)?; |
| 822 | } |
| 823 | } |
| 824 | |
| 825 | #[cfg (backtrace)] |
| 826 | { |
| 827 | use std::backtrace::BacktraceStatus; |
| 828 | |
| 829 | let backtrace = self |
| 830 | .backtrace |
| 831 | .as_ref() |
| 832 | .or_else(|| error.backtrace()) |
| 833 | .expect("backtrace capture failed" ); |
| 834 | if let BacktraceStatus::Captured = backtrace.status() { |
| 835 | write!(f, " \n\nStack backtrace: \n{}" , backtrace)?; |
| 836 | } |
| 837 | } |
| 838 | |
| 839 | Result::Ok(()) |
| 840 | } |
| 841 | |
| 842 | #[cfg (track_caller)] |
| 843 | fn track_caller(&mut self, location: &'static std::panic::Location<'static>) { |
| 844 | self.location = Some(location); |
| 845 | } |
| 846 | } |
| 847 | |
| 848 | /// Iterator of a chain of source errors. |
| 849 | /// |
| 850 | /// This type is the iterator returned by [`Report::chain`]. |
| 851 | /// |
| 852 | /// # Example |
| 853 | /// |
| 854 | /// ``` |
| 855 | /// use eyre::Report; |
| 856 | /// use std::io; |
| 857 | /// |
| 858 | /// pub fn underlying_io_error_kind(error: &Report) -> Option<io::ErrorKind> { |
| 859 | /// for cause in error.chain() { |
| 860 | /// if let Some(io_error) = cause.downcast_ref::<io::Error>() { |
| 861 | /// return Some(io_error.kind()); |
| 862 | /// } |
| 863 | /// } |
| 864 | /// None |
| 865 | /// } |
| 866 | /// ``` |
| 867 | #[derive (Clone)] |
| 868 | #[allow (missing_debug_implementations)] |
| 869 | pub struct Chain<'a> { |
| 870 | state: crate::chain::ChainState<'a>, |
| 871 | } |
| 872 | |
| 873 | /// type alias for `Result<T, Report>` |
| 874 | /// |
| 875 | /// This is a reasonable return type to use throughout your application but also for `fn main`; if |
| 876 | /// you do, failures will be printed along with a backtrace if one was captured. |
| 877 | /// |
| 878 | /// `eyre::Result` may be used with one *or* two type parameters. |
| 879 | /// |
| 880 | /// ```rust |
| 881 | /// use eyre::Result; |
| 882 | /// |
| 883 | /// # const IGNORE: &str = stringify! { |
| 884 | /// fn demo1() -> Result<T> {...} |
| 885 | /// // ^ equivalent to std::result::Result<T, eyre::Report> |
| 886 | /// |
| 887 | /// fn demo2() -> Result<T, OtherError> {...} |
| 888 | /// // ^ equivalent to std::result::Result<T, OtherError> |
| 889 | /// # }; |
| 890 | /// ``` |
| 891 | /// |
| 892 | /// # Example |
| 893 | /// |
| 894 | /// ``` |
| 895 | /// # pub trait Deserialize {} |
| 896 | /// # |
| 897 | /// # mod serde_json { |
| 898 | /// # use super::Deserialize; |
| 899 | /// # use std::io; |
| 900 | /// # |
| 901 | /// # pub fn from_str<T: Deserialize>(json: &str) -> io::Result<T> { |
| 902 | /// # unimplemented!() |
| 903 | /// # } |
| 904 | /// # } |
| 905 | /// # |
| 906 | /// # #[derive(Debug)] |
| 907 | /// # struct ClusterMap; |
| 908 | /// # |
| 909 | /// # impl Deserialize for ClusterMap {} |
| 910 | /// # |
| 911 | /// use eyre::Result; |
| 912 | /// |
| 913 | /// fn main() -> Result<()> { |
| 914 | /// # return Ok(()); |
| 915 | /// let config = std::fs::read_to_string("cluster.json" )?; |
| 916 | /// let map: ClusterMap = serde_json::from_str(&config)?; |
| 917 | /// println!("cluster info: {:#?}" , map); |
| 918 | /// Ok(()) |
| 919 | /// } |
| 920 | /// ``` |
| 921 | pub type Result<T, E = Report> = core::result::Result<T, E>; |
| 922 | |
| 923 | /// Provides the `wrap_err` method for `Result`. |
| 924 | /// |
| 925 | /// This trait is sealed and cannot be implemented for types outside of |
| 926 | /// `eyre`. |
| 927 | /// |
| 928 | /// # Example |
| 929 | /// |
| 930 | /// ``` |
| 931 | /// use eyre::{WrapErr, Result}; |
| 932 | /// use std::fs; |
| 933 | /// use std::path::PathBuf; |
| 934 | /// |
| 935 | /// pub struct ImportantThing { |
| 936 | /// path: PathBuf, |
| 937 | /// } |
| 938 | /// |
| 939 | /// impl ImportantThing { |
| 940 | /// # const IGNORE: &'static str = stringify! { |
| 941 | /// pub fn detach(&mut self) -> Result<()> {...} |
| 942 | /// # }; |
| 943 | /// # fn detach(&mut self) -> Result<()> { |
| 944 | /// # unimplemented!() |
| 945 | /// # } |
| 946 | /// } |
| 947 | /// |
| 948 | /// pub fn do_it(mut it: ImportantThing) -> Result<Vec<u8>> { |
| 949 | /// it.detach().wrap_err("Failed to detach the important thing" )?; |
| 950 | /// |
| 951 | /// let path = &it.path; |
| 952 | /// let content = fs::read(path) |
| 953 | /// .wrap_err_with(|| format!("Failed to read instrs from {}" , path.display()))?; |
| 954 | /// |
| 955 | /// Ok(content) |
| 956 | /// } |
| 957 | /// ``` |
| 958 | /// |
| 959 | /// When printed, the outermost error would be printed first and the lower |
| 960 | /// level underlying causes would be enumerated below. |
| 961 | /// |
| 962 | /// ```console |
| 963 | /// Error: Failed to read instrs from ./path/to/instrs.json |
| 964 | /// |
| 965 | /// Caused by: |
| 966 | /// No such file or directory (os error 2) |
| 967 | /// ``` |
| 968 | /// |
| 969 | /// # Wrapping Types That Don't impl `Error` (e.g. `&str` and `Box<dyn Error>`) |
| 970 | /// |
| 971 | /// Due to restrictions for coherence `Report` cannot impl `From` for types that don't impl |
| 972 | /// `Error`. Attempts to do so will give "this type might implement Error in the future" as an |
| 973 | /// error. As such, `wrap_err`, which uses `From` under the hood, cannot be used to wrap these |
| 974 | /// types. Instead we encourage you to use the combinators provided for `Result` in `std`/`core`. |
| 975 | /// |
| 976 | /// For example, instead of this: |
| 977 | /// |
| 978 | /// ```rust,compile_fail |
| 979 | /// use std::error::Error; |
| 980 | /// use eyre::{WrapErr, Report}; |
| 981 | /// |
| 982 | /// fn wrap_example(err: Result<(), Box<dyn Error + Send + Sync + 'static>>) -> Result<(), Report> { |
| 983 | /// err.wrap_err("saw a downstream error" ) |
| 984 | /// } |
| 985 | /// ``` |
| 986 | /// |
| 987 | /// We encourage you to write this: |
| 988 | /// |
| 989 | /// ```rust |
| 990 | /// use std::error::Error; |
| 991 | /// use eyre::{WrapErr, Report, eyre}; |
| 992 | /// |
| 993 | /// fn wrap_example(err: Result<(), Box<dyn Error + Send + Sync + 'static>>) -> Result<(), Report> { |
| 994 | /// err.map_err(|e| eyre!(e)).wrap_err("saw a downstream error" ) |
| 995 | /// } |
| 996 | /// ``` |
| 997 | /// |
| 998 | /// # Effect on downcasting |
| 999 | /// |
| 1000 | /// After attaching a message of type `D` onto an error of type `E`, the resulting |
| 1001 | /// `eyre::Report` may be downcast to `D` **or** to `E`. |
| 1002 | /// |
| 1003 | /// That is, in codebases that rely on downcasting, Eyre's wrap_err supports |
| 1004 | /// both of the following use cases: |
| 1005 | /// |
| 1006 | /// - **Attaching messages whose type is insignificant onto errors whose type |
| 1007 | /// is used in downcasts.** |
| 1008 | /// |
| 1009 | /// In other error libraries whose wrap_err is not designed this way, it can |
| 1010 | /// be risky to introduce messages to existing code because new message might |
| 1011 | /// break existing working downcasts. In Eyre, any downcast that worked |
| 1012 | /// before adding the message will continue to work after you add a message, so |
| 1013 | /// you should freely wrap errors wherever it would be helpful. |
| 1014 | /// |
| 1015 | /// ``` |
| 1016 | /// # use eyre::bail; |
| 1017 | /// # use thiserror::Error; |
| 1018 | /// # |
| 1019 | /// # #[derive(Error, Debug)] |
| 1020 | /// # #[error("???" )] |
| 1021 | /// # struct SuspiciousError; |
| 1022 | /// # |
| 1023 | /// # fn helper() -> Result<()> { |
| 1024 | /// # bail!(SuspiciousError); |
| 1025 | /// # } |
| 1026 | /// # |
| 1027 | /// use eyre::{WrapErr, Result}; |
| 1028 | /// |
| 1029 | /// fn do_it() -> Result<()> { |
| 1030 | /// helper().wrap_err("Failed to complete the work" )?; |
| 1031 | /// # const IGNORE: &str = stringify! { |
| 1032 | /// ... |
| 1033 | /// # }; |
| 1034 | /// # unreachable!() |
| 1035 | /// } |
| 1036 | /// |
| 1037 | /// fn main() { |
| 1038 | /// # #[cfg (not(feature = "auto-install" ))] |
| 1039 | /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); |
| 1040 | /// let err = do_it().unwrap_err(); |
| 1041 | /// if let Some(e) = err.downcast_ref::<SuspiciousError>() { |
| 1042 | /// // If helper() returned SuspiciousError, this downcast will |
| 1043 | /// // correctly succeed even with the message in between. |
| 1044 | /// # return; |
| 1045 | /// } |
| 1046 | /// # panic!("expected downcast to succeed" ); |
| 1047 | /// } |
| 1048 | /// ``` |
| 1049 | /// |
| 1050 | /// - **Attaching message whose type is used in downcasts onto errors whose |
| 1051 | /// type is insignificant.** |
| 1052 | /// |
| 1053 | /// Some codebases prefer to use machine-readable messages to categorize |
| 1054 | /// lower level errors in a way that will be actionable to higher levels of |
| 1055 | /// the application. |
| 1056 | /// |
| 1057 | /// ``` |
| 1058 | /// # use eyre::bail; |
| 1059 | /// # use thiserror::Error; |
| 1060 | /// # |
| 1061 | /// # #[derive(Error, Debug)] |
| 1062 | /// # #[error("???" )] |
| 1063 | /// # struct HelperFailed; |
| 1064 | /// # |
| 1065 | /// # fn helper() -> Result<()> { |
| 1066 | /// # bail!("no such file or directory" ); |
| 1067 | /// # } |
| 1068 | /// # |
| 1069 | /// use eyre::{WrapErr, Result}; |
| 1070 | /// |
| 1071 | /// fn do_it() -> Result<()> { |
| 1072 | /// helper().wrap_err(HelperFailed)?; |
| 1073 | /// # const IGNORE: &str = stringify! { |
| 1074 | /// ... |
| 1075 | /// # }; |
| 1076 | /// # unreachable!() |
| 1077 | /// } |
| 1078 | /// |
| 1079 | /// fn main() { |
| 1080 | /// # #[cfg (not(feature = "auto-install" ))] |
| 1081 | /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); |
| 1082 | /// let err = do_it().unwrap_err(); |
| 1083 | /// if let Some(e) = err.downcast_ref::<HelperFailed>() { |
| 1084 | /// // If helper failed, this downcast will succeed because |
| 1085 | /// // HelperFailed is the message that has been attached to |
| 1086 | /// // that error. |
| 1087 | /// # return; |
| 1088 | /// } |
| 1089 | /// # panic!("expected downcast to succeed" ); |
| 1090 | /// } |
| 1091 | /// ``` |
| 1092 | /// |
| 1093 | /// # `wrap_err` vs `wrap_err_with` |
| 1094 | /// |
| 1095 | /// `wrap_err` incurs a runtime cost even in the non-error case because it requires eagerly |
| 1096 | /// constructing the error object. `wrap_err_with` avoids this cost through lazy evaluation. This |
| 1097 | /// cost is proportional to the cost of the currently installed [`EyreHandler`]'s creation step. |
| 1098 | /// `wrap_err` is useful in cases where an constructed error object already exists. |
| 1099 | pub trait WrapErr<T, E>: context::private::Sealed { |
| 1100 | /// Wrap the error value with a new adhoc error |
| 1101 | #[cfg_attr (track_caller, track_caller)] |
| 1102 | fn wrap_err<D>(self, msg: D) -> Result<T, Report> |
| 1103 | where |
| 1104 | D: Display + Send + Sync + 'static; |
| 1105 | |
| 1106 | /// Wrap the error value with a new adhoc error that is evaluated lazily |
| 1107 | /// only once an error does occur. |
| 1108 | #[cfg_attr (track_caller, track_caller)] |
| 1109 | fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report> |
| 1110 | where |
| 1111 | D: Display + Send + Sync + 'static, |
| 1112 | F: FnOnce() -> D; |
| 1113 | |
| 1114 | /// Compatibility re-export of wrap_err for interop with `anyhow` |
| 1115 | #[cfg_attr (track_caller, track_caller)] |
| 1116 | fn context<D>(self, msg: D) -> Result<T, Report> |
| 1117 | where |
| 1118 | D: Display + Send + Sync + 'static; |
| 1119 | |
| 1120 | /// Compatibility re-export of wrap_err_with for interop with `anyhow` |
| 1121 | #[cfg_attr (track_caller, track_caller)] |
| 1122 | fn with_context<D, F>(self, f: F) -> Result<T, Report> |
| 1123 | where |
| 1124 | D: Display + Send + Sync + 'static, |
| 1125 | F: FnOnce() -> D; |
| 1126 | } |
| 1127 | |
| 1128 | /// Provides the [`ok_or_eyre`][OptionExt::ok_or_eyre] method for [`Option`]. |
| 1129 | /// |
| 1130 | /// This trait is sealed and cannot be implemented for types outside of |
| 1131 | /// `eyre`. |
| 1132 | /// |
| 1133 | /// # Example |
| 1134 | /// |
| 1135 | /// ``` |
| 1136 | /// # #[cfg (not(feature = "auto-install" ))] |
| 1137 | /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); |
| 1138 | /// use eyre::OptionExt; |
| 1139 | /// |
| 1140 | /// let option: Option<()> = None; |
| 1141 | /// |
| 1142 | /// let result = option.ok_or_eyre("static str error" ); |
| 1143 | /// |
| 1144 | /// assert_eq!(result.unwrap_err().to_string(), "static str error" ); |
| 1145 | /// ``` |
| 1146 | /// |
| 1147 | /// # `ok_or_eyre` vs `ok_or_else` |
| 1148 | /// |
| 1149 | /// If string interpolation is required for the generated [report][Report], |
| 1150 | /// use [`ok_or_else`][Option::ok_or_else] instead, |
| 1151 | /// invoking [`eyre!`] to perform string interpolation: |
| 1152 | /// |
| 1153 | /// ``` |
| 1154 | /// # #[cfg (not(feature = "auto-install" ))] |
| 1155 | /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); |
| 1156 | /// use eyre::eyre; |
| 1157 | /// |
| 1158 | /// let option: Option<()> = None; |
| 1159 | /// |
| 1160 | /// let result = option.ok_or_else(|| eyre!("{} error" , "dynamic" )); |
| 1161 | /// |
| 1162 | /// assert_eq!(result.unwrap_err().to_string(), "dynamic error" ); |
| 1163 | /// ``` |
| 1164 | /// |
| 1165 | /// `ok_or_eyre` incurs no runtime cost, as the error object |
| 1166 | /// is constructed from the provided static argument |
| 1167 | /// only in the `None` case. |
| 1168 | pub trait OptionExt<T>: context::private::Sealed { |
| 1169 | /// Transform the [`Option<T>`] into a [`Result<T, E>`], |
| 1170 | /// mapping [`Some(v)`][Option::Some] to [`Ok(v)`][Result::Ok] |
| 1171 | /// and [`None`] to [`Report`]. |
| 1172 | /// |
| 1173 | /// `ok_or_eyre` allows for eyre [`Report`] error objects |
| 1174 | /// to be lazily created from static messages in the `None` case. |
| 1175 | /// |
| 1176 | /// For dynamic error messages, use [`ok_or_else`][Option::ok_or_else], |
| 1177 | /// invoking [`eyre!`] in the closure to perform string interpolation. |
| 1178 | fn ok_or_eyre<M>(self, message: M) -> crate::Result<T> |
| 1179 | where |
| 1180 | M: Debug + Display + Send + Sync + 'static; |
| 1181 | } |
| 1182 | |
| 1183 | /// Provides the `context` method for `Option` when porting from `anyhow` |
| 1184 | /// |
| 1185 | /// This trait is sealed and cannot be implemented for types outside of |
| 1186 | /// `eyre`. |
| 1187 | /// |
| 1188 | /// ## Why Doesn't `Eyre` impl `WrapErr` for `Option`? |
| 1189 | /// |
| 1190 | /// `eyre` doesn't impl `WrapErr` for `Option` because `wrap_err` implies that you're creating a |
| 1191 | /// new error that saves the previous error as its `source`. Calling `wrap_err` on an `Option` is |
| 1192 | /// meaningless because there is no source error. `anyhow` avoids this issue by using a different |
| 1193 | /// mental model where you're adding "context" to an error, though this not a mental model for |
| 1194 | /// error handling that `eyre` agrees with. |
| 1195 | /// |
| 1196 | /// Instead, `eyre` encourages users to think of each error as distinct, where the previous error |
| 1197 | /// is the context being saved by the new error, which is backwards compared to anyhow's model. In |
| 1198 | /// this model you're encouraged to use combinators provided by `std` for `Option` to convert an |
| 1199 | /// option to a `Result` |
| 1200 | /// |
| 1201 | /// # Example |
| 1202 | /// |
| 1203 | /// Instead of: |
| 1204 | /// |
| 1205 | /// ```rust |
| 1206 | /// use eyre::ContextCompat; |
| 1207 | /// |
| 1208 | /// fn get_thing(mut things: impl Iterator<Item = u32>) -> eyre::Result<u32> { |
| 1209 | /// things |
| 1210 | /// .find(|&thing| thing == 42) |
| 1211 | /// .context("the thing wasnt in the list" ) |
| 1212 | /// } |
| 1213 | /// ``` |
| 1214 | /// |
| 1215 | /// We encourage you to use this: |
| 1216 | /// |
| 1217 | /// ```rust |
| 1218 | /// use eyre::eyre; |
| 1219 | /// |
| 1220 | /// fn get_thing(mut things: impl Iterator<Item = u32>) -> eyre::Result<u32> { |
| 1221 | /// things |
| 1222 | /// .find(|&thing| thing == 42) |
| 1223 | /// .ok_or_else(|| eyre!("the thing wasnt in the list" )) |
| 1224 | /// } |
| 1225 | /// ``` |
| 1226 | pub trait ContextCompat<T>: context::private::Sealed { |
| 1227 | /// Compatibility version of `wrap_err` for creating new errors with new source on `Option` |
| 1228 | /// when porting from `anyhow` |
| 1229 | #[cfg_attr (track_caller, track_caller)] |
| 1230 | fn context<D>(self, msg: D) -> Result<T, Report> |
| 1231 | where |
| 1232 | D: Display + Send + Sync + 'static; |
| 1233 | |
| 1234 | /// Compatibility version of `wrap_err_with` for creating new errors with new source on `Option` |
| 1235 | /// when porting from `anyhow` |
| 1236 | #[cfg_attr (track_caller, track_caller)] |
| 1237 | fn with_context<D, F>(self, f: F) -> Result<T, Report> |
| 1238 | where |
| 1239 | D: Display + Send + Sync + 'static, |
| 1240 | F: FnOnce() -> D; |
| 1241 | |
| 1242 | /// Compatibility re-export of `context` for porting from `anyhow` to `eyre` |
| 1243 | #[cfg_attr (track_caller, track_caller)] |
| 1244 | fn wrap_err<D>(self, msg: D) -> Result<T, Report> |
| 1245 | where |
| 1246 | D: Display + Send + Sync + 'static; |
| 1247 | |
| 1248 | /// Compatibility re-export of `with_context` for porting from `anyhow` to `eyre` |
| 1249 | #[cfg_attr (track_caller, track_caller)] |
| 1250 | fn wrap_err_with<D, F>(self, f: F) -> Result<T, Report> |
| 1251 | where |
| 1252 | D: Display + Send + Sync + 'static, |
| 1253 | F: FnOnce() -> D; |
| 1254 | } |
| 1255 | |
| 1256 | /// Equivalent to Ok::<_, eyre::Error>(value). |
| 1257 | /// |
| 1258 | /// This simplifies creation of an eyre::Result in places where type inference |
| 1259 | /// cannot deduce the `E` type of the result — without needing to write |
| 1260 | /// `Ok::<_, eyre::Error>(value)`. |
| 1261 | /// |
| 1262 | /// One might think that `eyre::Result::Ok(value)` would work in such cases |
| 1263 | /// but it does not. |
| 1264 | /// |
| 1265 | /// ```console |
| 1266 | /// error[E0282]: type annotations needed for `std::result::Result<i32, E>` |
| 1267 | /// --> src/main.rs:11:13 |
| 1268 | /// | |
| 1269 | /// 11 | let _ = eyre::Result::Ok(1); |
| 1270 | /// | - ^^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result` |
| 1271 | /// | | |
| 1272 | /// | consider giving this pattern the explicit type `std::result::Result<i32, E>`, where the type parameter `E` is specified |
| 1273 | /// ``` |
| 1274 | #[allow (non_snake_case)] |
| 1275 | pub fn Ok<T>(t: T) -> Result<T> { |
| 1276 | Result::Ok(t) |
| 1277 | } |
| 1278 | |
| 1279 | // Not public API. Referenced by macro-generated code. |
| 1280 | #[doc (hidden)] |
| 1281 | pub mod private { |
| 1282 | use crate::Report; |
| 1283 | use alloc::fmt; |
| 1284 | use core::fmt::{Arguments, Debug, Display}; |
| 1285 | |
| 1286 | pub use alloc::format; |
| 1287 | pub use core::format_args; |
| 1288 | pub use core::result::Result::Err; |
| 1289 | |
| 1290 | #[doc (hidden)] |
| 1291 | pub mod kind { |
| 1292 | pub use crate::kind::{AdhocKind, TraitKind}; |
| 1293 | |
| 1294 | pub use crate::kind::BoxedKind; |
| 1295 | } |
| 1296 | |
| 1297 | #[cfg_attr (track_caller, track_caller)] |
| 1298 | pub fn new_adhoc<M>(message: M) -> Report |
| 1299 | where |
| 1300 | M: Display + Debug + Send + Sync + 'static, |
| 1301 | { |
| 1302 | Report::from_adhoc(message) |
| 1303 | } |
| 1304 | |
| 1305 | #[doc (hidden)] |
| 1306 | #[cold ] |
| 1307 | #[cfg_attr (track_caller, track_caller)] |
| 1308 | pub fn format_err(args: Arguments<'_>) -> Report { |
| 1309 | #[cfg (eyre_no_fmt_arguments_as_str)] |
| 1310 | let fmt_arguments_as_str: Option<&str> = None; |
| 1311 | #[cfg (not(eyre_no_fmt_arguments_as_str))] |
| 1312 | let fmt_arguments_as_str = args.as_str(); |
| 1313 | |
| 1314 | if let Some(message) = fmt_arguments_as_str { |
| 1315 | // eyre!("literal"), can downcast to &'static str |
| 1316 | Report::msg(message) |
| 1317 | } else { |
| 1318 | // eyre!("interpolate {var}"), can downcast to String |
| 1319 | Report::msg(fmt::format(args)) |
| 1320 | } |
| 1321 | } |
| 1322 | } |
| 1323 | |