eyre-0.6.12/.cargo_vcs_info.json0000644000000001420000000000100120630ustar { "git": { "sha1": "701d05a9f6fb4812a8561772da65066dc007faa1" }, "path_in_vcs": "eyre" }eyre-0.6.12/CHANGELOG.md000064400000000000000000000077021046102023000124750ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - ReleaseDate ## [0.6.12] - 2024-01-31 ### Fixed - Unsound cast to invalid type during Report downcast [by ten3roberts](https://github.com/eyre-rs/eyre/pull/143) ## [0.6.11] - 2023-12-13 ### Fixed - stale references to `Error` in docstrings [by birkenfeld](https://github.com/eyre-rs/eyre/pull/87) ### Added - one-argument ensure!($expr) [by sharnoff](https://github.com/eyre-rs/eyre/pull/86) - documentation on the performance characteristics of `wrap_err` vs `wrap_err_with` [by akshayknarayan](https://github.com/eyre-rs/eyre/pull/93) - tl;dr: `wrap_err_with` is faster unless the constructed error object already exists - ~~automated conversion to external errors for ensure! and bail! [by j-baker](https://github.com/eyre-rs/eyre/pull/95)~~ breaking change: shelved for next major release - eyre::Ok for generating eyre::Ok() without fully specifying the type [by kylewlacy](https://github.com/eyre-rs/eyre/pull/91) - `OptionExt::ok_or_eyre` for yielding static `Report`s from `None` [by LeoniePhiline](https://github.com/eyre-rs/eyre/pull/125) ### New Contributors - @sharnoff made their first contribution in https://github.com/eyre-rs/eyre/pull/86 - @akshayknarayan made their first contribution in https://github.com/eyre-rs/eyre/pull/93 - @j-baker made their first contribution in https://github.com/eyre-rs/eyre/pull/95 - @kylewlacy made their first contribution in https://github.com/eyre-rs/eyre/pull/91 - @LeoniePhiline made their first contribution in https://github.com/eyre-rs/eyre/pull/129 ~~## [0.6.10] - 2023-12-07~~ Yanked ## [0.6.9] - 2023-11-17 ### Fixed - stacked borrows when dropping [by TimDiekmann](https://github.com/eyre-rs/eyre/pull/81) - miri validation errors through now stricter provenance [by ten3roberts](https://github.com/eyre-rs/eyre/pull/103) - documentation on no_std support [by thenorili](https://github.com/eyre-rs/eyre/pull/111) ### Added - monorepo for eyre-related crates [by pksunkara](https://github.com/eyre-rs/eyre/pull/104), [[2]](https://github.com/eyre-rs/eyre/pull/105)[[3]](https://github.com/eyre-rs/eyre/pull/107) - CONTRIBUTING.md [by yaahc](https://github.com/eyre-rs/eyre/pull/99) ## [0.6.8] - 2022-04-04 ### Added - `#[must_use]` to `Report` - `must-install` feature to help reduce binary sizes when using a custom `EyreHandler` ## [0.6.7] - 2022-02-24 ### Fixed - missing track_caller annotation to new format arg capture constructor ## [0.6.6] - 2022-01-19 ### Added - support for format arguments capture on 1.58 and later ## [0.6.5] - 2021-01-05 ### Added - optional support for converting into `pyo3` exceptions ## [0.6.4] - 2021-01-04 ### Fixed - missing track_caller annotations to `wrap_err` related trait methods ## [0.6.3] - 2020-11-10 ### Fixed - missing track_caller annotation to autoref specialization functions ## [0.6.2] - 2020-10-27 ### Fixed - missing track_caller annotation to new_adhoc function ## [0.6.1] - 2020-09-28 ### Added - support for track_caller on rust versions where it is available [Unreleased]: https://github.com/eyre-rs/eyre/compare/v0.6.11...HEAD [0.6.11]: https://github.com/eyre-rs/eyre/compare/v0.6.9...v0.6.11 [0.6.9]: https://github.com/eyre-rs/eyre/compare/v0.6.8...v0.6.9 [0.6.8]: https://github.com/eyre-rs/eyre/compare/v0.6.7...v0.6.8 [0.6.7]: https://github.com/eyre-rs/eyre/compare/v0.6.6...v0.6.7 [0.6.6]: https://github.com/eyre-rs/eyre/compare/v0.6.5...v0.6.6 [0.6.5]: https://github.com/eyre-rs/eyre/compare/v0.6.4...v0.6.5 [0.6.4]: https://github.com/eyre-rs/eyre/compare/v0.6.3...v0.6.4 [0.6.3]: https://github.com/eyre-rs/eyre/compare/v0.6.2...v0.6.3 [0.6.2]: https://github.com/eyre-rs/eyre/compare/v0.6.1...v0.6.2 [0.6.1]: https://github.com/eyre-rs/eyre/releases/tag/v0.6.1 eyre-0.6.12/Cargo.lock0000644000000316630000000000100100520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "basic-toml" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" dependencies = [ "serde", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "dissimilar" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "eyre" version = "0.6.12" dependencies = [ "anyhow", "backtrace", "futures", "indenter", "once_cell", "pyo3", "rustversion", "syn", "thiserror", "trybuild", ] [[package]] name = "futures" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-io" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-sink" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", "futures-sink", "futures-task", "pin-project-lite", "pin-utils", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "object" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parking_lot" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", "instant", "libc", "redox_syscall", "smallvec", "winapi", ] [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b" dependencies = [ "cfg-if", "libc", "memoffset", "parking_lot", "pyo3-build-config", "pyo3-ffi", ] [[package]] name = "pyo3-build-config" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5" dependencies = [ "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b" dependencies = [ "libc", "pyo3-build-config", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustversion" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-lexicon" version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "termcolor" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "trybuild" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "196a58260a906cedb9bf6d8034b6379d0c11f552416960452f267402ceeddff1" dependencies = [ "basic-toml", "dissimilar", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" eyre-0.6.12/Cargo.toml0000644000000034550000000000100100730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.65.0" name = "eyre" version = "0.6.12" authors = [ "David Tolnay ", "Jane Lusby ", ] description = "Flexible concrete Error Reporting type built on std::error::Error with customizable Reports" documentation = "https://docs.rs/eyre" readme = "README.md" categories = ["rust-patterns"] license = "MIT OR Apache-2.0" repository = "https://github.com/eyre-rs/eyre" [package.metadata.docs.rs] rustdoc-args = [ "--cfg", "doc_cfg", ] targets = ["x86_64-unknown-linux-gnu"] [package.metadata.workspaces] independent = true [dependencies.indenter] version = "0.3.0" [dependencies.once_cell] version = "1.18.0" [dependencies.pyo3] version = "0.20" optional = true default-features = false [dev-dependencies.anyhow] version = "1.0.28" [dev-dependencies.backtrace] version = "0.3.46" [dev-dependencies.futures] version = "0.3" default-features = false [dev-dependencies.pyo3] version = "0.20" features = ["auto-initialize"] default-features = false [dev-dependencies.rustversion] version = "1.0" [dev-dependencies.syn] version = "2.0" features = ["full"] [dev-dependencies.thiserror] version = "1.0" [dev-dependencies.trybuild] version = "1.0.19" features = ["diff"] [features] auto-install = [] default = [ "auto-install", "track-caller", ] track-caller = [] eyre-0.6.12/Cargo.toml.orig000064400000000000000000000022641046102023000135510ustar 00000000000000[package] name = "eyre" version = "0.6.12" authors = ["David Tolnay ", "Jane Lusby "] description = "Flexible concrete Error Reporting type built on std::error::Error with customizable Reports" documentation = "https://docs.rs/eyre" categories = ["rust-patterns"] edition = { workspace = true } license = { workspace = true } repository = { workspace = true } readme = { workspace = true } rust-version = { workspace = true } [features] default = ["auto-install", "track-caller"] auto-install = [] track-caller = [] [dependencies] indenter = { workspace = true } once_cell = { workspace = true } pyo3 = { version = "0.20", optional = true, default-features = false } [dev-dependencies] futures = { version = "0.3", default-features = false } rustversion = "1.0" thiserror = "1.0" trybuild = { version = "1.0.19", features = ["diff"] } backtrace = "0.3.46" anyhow = "1.0.28" syn = { version = "2.0", features = ["full"] } pyo3 = { version = "0.20", default-features = false, features = ["auto-initialize"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--cfg", "doc_cfg"] [package.metadata.workspaces] independent = true eyre-0.6.12/LICENSE-APACHE000064400000000000000000000251371046102023000126120ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. eyre-0.6.12/LICENSE-MIT000064400000000000000000000017771046102023000123260ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. eyre-0.6.12/README.md000064400000000000000000000246631046102023000121500ustar 00000000000000eyre ==== [![Build Status][actions-badge]][actions-url] [![Latest Version](https://img.shields.io/crates/v/eyre.svg)](https://crates.io/crates/eyre) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/eyre) [![Discord chat][discord-badge]][discord-url] [actions-badge]: https://github.com/eyre-rs/eyre/workflows/Continuous%20integration/badge.svg [actions-url]: https://github.com/eyre-rs/eyre/actions?query=workflow%3A%22Continuous+integration%22 [discord-badge]: https://img.shields.io/discord/960645145018110012?label=eyre%20community%20discord [discord-url]: https://discord.gg/z94RqmUTKB This library provides [`eyre::Report`][Report], a trait object based error handling type for easy idiomatic error handling and reporting in Rust applications. This crate is a fork of [`anyhow`] with support for customized error reports. For more details on customization checkout the docs on [`eyre::EyreHandler`]. ## Custom Report Handlers The heart of this crate is its ability to swap out the Handler type to change what information is carried alongside errors and how the end report is formatted. This crate is meant to be used alongside companion crates that customize its behavior. Below is a list of known crates that export report handlers for eyre and short summaries of what features they provide. - [`stable-eyre`]: Switches the backtrace type from `std`'s to `backtrace-rs`'s so that it can be captured on stable. The report format is identical to `DefaultHandler`'s report format. - [`color-eyre`]: Captures a `backtrace::Backtrace` and a `tracing_error::SpanTrace`. Provides a `Help` trait for attaching warnings and suggestions to error reports. The end report is then pretty printed with the help of [`color-backtrace`], [`color-spantrace`], and `ansi_term`. Check out the README on [`color-eyre`] for details on the report format. - [`simple-eyre`]: A minimal `EyreHandler` that captures no additional information, for when you do not wish to capture `Backtrace`s with errors. - [`jane-eyre`]: A report handler crate that exists purely for the pun of it. Currently just re-exports `color-eyre`. ## Usage Recommendations and Stability Considerations **We recommend users do not re-export types from this library as part their own public API for libraries with external users.** The main reason for this is that it will make your library API break if we ever bump the major version number on eyre and your users upgrade the eyre version they use in their application code before you upgrade your own eyre dep version[^1]. However, even beyond this API stability hazard, there are other good reasons to avoid using `eyre::Report` as your public error type. - You export an undocumented error interface that is otherwise still accessible via downcast, making it hard for users to react to specific errors while not preventing them from depending on details you didn't mean to make part of your public API. - This in turn makes the error types of all libraries you use a part of your public API as well, and makes changing any of those libraries into an undetectable runtime breakage. - If many of your errors are constructed from strings you encourage your users to use string comparision for reacting to specific errors which is brittle and turns updating error messages into a potentially undetectable runtime breakage. ## Details - Use `Result`, or equivalently `eyre::Result`, as the return type of any fallible function. Within the function, use `?` to easily propagate any error that implements the `std::error::Error` trait. ```rust use eyre::Result; fn get_cluster_info() -> Result { let config = std::fs::read_to_string("cluster.json")?; let map: ClusterMap = serde_json::from_str(&config)?; Ok(map) } ``` - Wrap a lower level error with a new error created from a message to help the person troubleshooting understand the chain of failures that occurred. A low-level error like "No such file or directory" can be annoying to debug without more information about what higher level step the application was in the middle of. ```rust use eyre::{WrapErr, Result}; fn main() -> Result<()> { ... it.detach().wrap_err("Failed to detach the important thing")?; let content = std::fs::read(path) .wrap_err_with(|| format!("Failed to read instrs from {}", path))?; ... } ``` ```console Error: Failed to read instrs from ./path/to/instrs.json Caused by: No such file or directory (os error 2) ``` - Downcasting is supported and can be by value, by shared reference, or by mutable reference as needed. ```rust // If the error was caused by redaction, then return a // tombstone instead of the content. match root_cause.downcast_ref::() { Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), None => Err(error), } ``` - If using the nightly channel, a backtrace is captured and printed with the error if the underlying error type does not already provide its own. In order to see backtraces, they must be enabled through the environment variables described in [`std::backtrace`]: - If you want panics and errors to both have backtraces, set `RUST_BACKTRACE=1`; - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and `RUST_LIB_BACKTRACE=0`. The tracking issue for this feature is [rust-lang/rust#53487]. [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487 - Eyre works with any error type that has an impl of `std::error::Error`, including ones defined in your crate. We do not bundle a `derive(Error)` macro but you can write the impls yourself or use a standalone macro like [thiserror]. ```rust use thiserror::Error; #[derive(Error, Debug)] pub enum FormatError { #[error("Invalid header (expected {expected:?}, got {found:?})")] InvalidHeader { expected: String, found: String, }, #[error("Missing attribute: {0}")] MissingAttribute(String), } ``` - One-off error messages can be constructed using the `eyre!` macro, which supports string interpolation and produces an `eyre::Report`. ```rust return Err(eyre!("Missing attribute: {}", missing)); ``` - On newer versions of the compiler (e.g. 1.58 and later) this macro also supports format args captures. ```rust return Err(eyre!("Missing attribute: {missing}")); ``` ## No-std support No-std support was removed in 2020 in [commit 608a16a] due to unaddressed upstream breakages. [commit 608a16a]: https://github.com/eyre-rs/eyre/pull/29/commits/608a16aa2c2c27eca6c88001cc94c6973c18f1d5 ## Comparison to failure The `eyre::Report` type works something like `failure::Error`, but unlike failure ours is built around the standard library's `std::error::Error` trait rather than a separate trait `failure::Fail`. The standard library has adopted the necessary improvements for this to be possible as part of [RFC 2504]. [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md ## Comparison to thiserror Use `eyre` if you don't think you'll do anything with an error other than report it. This is common in application code. Use `thiserror` if you think you need an error type that can be handled via match or reported. This is common in library crates where you don't know how your users will handle your errors. [thiserror]: https://github.com/dtolnay/thiserror ## Compatibility with `anyhow` This crate does its best to be usable as a drop in replacement of `anyhow` and vice-versa by `re-exporting` all of the renamed APIs with the names used in `anyhow`, though there are some differences still. #### `Context` and `Option` As part of renaming `Context` to `WrapErr` we also intentionally do not implement `WrapErr` for `Option`. This decision was made because `wrap_err` implies that you're creating a new error that saves the old error as its `source`. With `Option` there is no source error to wrap, so `wrap_err` ends up being somewhat meaningless. Instead `eyre` offers [`OptionExt::ok_or_eyre`] to yield _static_ errors from `None`, and intends for users to use the combinator functions provided by `std`, converting `Option`s to `Result`s, for _dynamic_ errors. So where you would write this with anyhow: [`OptionExt::ok_or_eyre`]: https://docs.rs/eyre/latest/eyre/trait.OptionExt.html#tymethod.ok_or_eyre ```rust use anyhow::Context; let opt: Option<()> = None; let result_static = opt.context("static error message"); let result_dynamic = opt.with_context(|| format!("{} error message", "dynamic")); ``` With `eyre` we want users to write: ```rust use eyre::{eyre, OptionExt, Result}; let opt: Option<()> = None; let result_static: Result<()> = opt.ok_or_eyre("static error message"); let result_dynamic: Result<()> = opt.ok_or_else(|| eyre!("{} error message", "dynamic")); ``` **NOTE**: However, to help with porting we do provide a `ContextCompat` trait which implements `context` for options which you can import to make existing `.context` calls compile. [Report]: https://docs.rs/eyre/*/eyre/struct.Report.html [`eyre::EyreHandler`]: https://docs.rs/eyre/*/eyre/trait.EyreHandler.html [`eyre::WrapErr`]: https://docs.rs/eyre/*/eyre/trait.WrapErr.html [`anyhow::Context`]: https://docs.rs/anyhow/*/anyhow/trait.Context.html [`anyhow`]: https://github.com/dtolnay/anyhow [`tracing_error::SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html [`stable-eyre`]: https://github.com/eyre-rs/stable-eyre [`color-eyre`]: https://github.com/eyre-rs/color-eyre [`jane-eyre`]: https://github.com/yaahc/jane-eyre [`simple-eyre`]: https://github.com/eyre-rs/simple-eyre [`color-spantrace`]: https://github.com/eyre-rs/color-spantrace [`color-backtrace`]: https://github.com/athre0z/color-backtrace [^1]: example and explanation of breakage https://github.com/eyre-rs/eyre/issues/30#issuecomment-647650361 #### License Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. eyre-0.6.12/build.rs000064400000000000000000000070161046102023000123270ustar 00000000000000use std::env; use std::ffi::OsString; use std::fs; use std::path::Path; use std::process::{Command, ExitStatus}; use std::str; // This code exercises the surface area that we expect of the std Backtrace // type. If the current toolchain is able to compile it, we go ahead and use // backtrace in eyre. const BACKTRACE_PROBE: &str = r#" #![feature(backtrace)] #![allow(dead_code)] use std::backtrace::{Backtrace, BacktraceStatus}; use std::error::Error; use std::fmt::{self, Display}; #[derive(Debug)] struct E; impl Display for E { fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { unimplemented!() } } impl Error for E { fn backtrace(&self) -> Option<&Backtrace> { let backtrace = Backtrace::capture(); match backtrace.status() { BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} } unimplemented!() } } "#; const TRACK_CALLER_PROBE: &str = r#" #![allow(dead_code)] #[track_caller] fn foo() { let _location = std::panic::Location::caller(); } "#; fn main() { match compile_probe(BACKTRACE_PROBE) { Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), _ => {} } match compile_probe(TRACK_CALLER_PROBE) { Some(status) if status.success() => println!("cargo:rustc-cfg=track_caller"), _ => {} } let version = match rustc_version_info() { Some(version) => version, None => return, }; version.toolchain.set_feature(); if version.minor < 52 { println!("cargo:rustc-cfg=eyre_no_fmt_arguments_as_str"); } if version.minor < 58 { println!("cargo:rustc-cfg=eyre_no_fmt_args_capture"); } } fn compile_probe(probe: &str) -> Option { let rustc = env::var_os("RUSTC")?; let out_dir = env::var_os("OUT_DIR")?; let probefile = Path::new(&out_dir).join("probe.rs"); fs::write(&probefile, probe).ok()?; Command::new(rustc) .arg("--edition=2018") .arg("--crate-name=eyre_build") .arg("--crate-type=lib") .arg("--emit=metadata") .arg("--out-dir") .arg(out_dir) .arg(probefile) .status() .ok() } // TODO factor this toolchain parsing and related tests into its own file #[derive(PartialEq)] enum Toolchain { Stable, Beta, Nightly, } impl Toolchain { fn set_feature(self) { match self { Toolchain::Nightly => println!("cargo:rustc-cfg=nightly"), Toolchain::Beta => println!("cargo:rustc-cfg=beta"), Toolchain::Stable => println!("cargo:rustc-cfg=stable"), } } } struct VersionInfo { minor: u32, toolchain: Toolchain, } fn rustc_version_info() -> Option { let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); let output = Command::new(rustc).arg("--version").output().ok()?; let version = str::from_utf8(&output.stdout).ok()?; let mut pieces = version.split(['.', ' ', '-']); if pieces.next() != Some("rustc") { return None; } let _major: u32 = pieces.next()?.parse().ok()?; let minor = pieces.next()?.parse().ok()?; let _patch: u32 = pieces.next()?.parse().ok()?; let toolchain = match pieces.next() { Some("beta") => Toolchain::Beta, Some("nightly") => Toolchain::Nightly, _ => Toolchain::Stable, }; let version = VersionInfo { minor, toolchain }; Some(version) } eyre-0.6.12/examples/custom_handler.rs000064400000000000000000000042431046102023000160540ustar 00000000000000use backtrace::Backtrace; use eyre::EyreHandler; use std::error::Error; use std::{fmt, iter}; fn main() -> eyre::Result<()> { // Install our custom eyre report hook for constructing our custom Handlers install().unwrap(); // construct a report with, hopefully, our custom handler! let mut report = eyre::eyre!("hello from custom error town!"); // manually set the custom msg for this report after it has been constructed if let Some(handler) = report.handler_mut().downcast_mut::() { handler.custom_msg = Some("you're the best users, you know that right???"); } // print that shit!! Err(report) } // define a handler that captures backtraces unless told not to fn install() -> Result<(), impl Error> { let capture_backtrace = std::env::var("RUST_BACKWARDS_TRACE") .map(|val| val != "0") .unwrap_or(true); let hook = Hook { capture_backtrace }; eyre::set_hook(Box::new(move |e| Box::new(hook.make_handler(e)))) } struct Hook { capture_backtrace: bool, } impl Hook { fn make_handler(&self, _error: &(dyn Error + 'static)) -> Handler { let backtrace = if self.capture_backtrace { Some(Backtrace::new()) } else { None }; Handler { backtrace, custom_msg: None, } } } struct Handler { // custom configured backtrace capture backtrace: Option, // customizable message payload associated with reports custom_msg: Option<&'static str>, } impl EyreHandler for Handler { fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { return fmt::Debug::fmt(error, f); } let errors = iter::successors(Some(error), |error| (*error).source()); for (ind, error) in errors.enumerate() { write!(f, "\n{:>4}: {}", ind, error)?; } if let Some(backtrace) = self.backtrace.as_ref() { writeln!(f, "\n\nBacktrace:\n{:?}", backtrace)?; } if let Some(msg) = self.custom_msg.as_ref() { writeln!(f, "\n\n{}", msg)?; } Ok(()) } } eyre-0.6.12/examples/eyre-usage.rs000064400000000000000000000003171046102023000151110ustar 00000000000000use eyre::{eyre, Report, WrapErr}; fn main() -> Result<(), Report> { let e: Report = eyre!("oh no this program is just bad!"); Err(e).wrap_err("usage example successfully experienced a failure") } eyre-0.6.12/src/backtrace.rs000064400000000000000000000006431046102023000137350ustar 00000000000000#[cfg(backtrace)] pub(crate) use std::backtrace::Backtrace; #[cfg(not(backtrace))] pub(crate) enum Backtrace {} #[cfg(backtrace)] macro_rules! backtrace_if_absent { ($err:expr) => { match $err.backtrace() { Some(_) => None, None => Some(Backtrace::capture()), } }; } #[cfg(not(backtrace))] macro_rules! backtrace_if_absent { ($err:expr) => { None }; } eyre-0.6.12/src/chain.rs000064400000000000000000000053141046102023000131000ustar 00000000000000use self::ChainState::*; use crate::StdError; use std::vec; pub(crate) use crate::Chain; #[derive(Clone)] pub(crate) enum ChainState<'a> { Linked { next: Option<&'a (dyn StdError + 'static)>, }, Buffered { rest: vec::IntoIter<&'a (dyn StdError + 'static)>, }, } impl<'a> Chain<'a> { /// Construct an iterator over a chain of errors via the `source` method /// /// # Example /// /// ```rust /// use std::error::Error; /// use std::fmt::{self, Write}; /// use eyre::Chain; /// use indenter::indented; /// /// fn report(error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { /// let mut errors = Chain::new(error).enumerate(); /// for (i, error) in errors { /// writeln!(f)?; /// write!(indented(f).ind(i), "{}", error)?; /// } /// /// Ok(()) /// } /// ``` pub fn new(head: &'a (dyn StdError + 'static)) -> Self { Chain { state: ChainState::Linked { next: Some(head) }, } } } impl<'a> Iterator for Chain<'a> { type Item = &'a (dyn StdError + 'static); fn next(&mut self) -> Option { match &mut self.state { Linked { next } => { let error = (*next)?; *next = error.source(); Some(error) } Buffered { rest } => rest.next(), } } fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } } impl DoubleEndedIterator for Chain<'_> { fn next_back(&mut self) -> Option { match &mut self.state { Linked { mut next } => { let mut rest = Vec::new(); while let Some(cause) = next { next = cause.source(); rest.push(cause); } let mut rest = rest.into_iter(); let last = rest.next_back(); self.state = Buffered { rest }; last } Buffered { rest } => rest.next_back(), } } } impl ExactSizeIterator for Chain<'_> { fn len(&self) -> usize { match &self.state { Linked { mut next } => { let mut len = 0; while let Some(cause) = next { next = cause.source(); len += 1; } len } Buffered { rest } => rest.len(), } } } impl Default for Chain<'_> { fn default() -> Self { Chain { state: ChainState::Buffered { rest: Vec::new().into_iter(), }, } } } eyre-0.6.12/src/context.rs000064400000000000000000000103061046102023000134770ustar 00000000000000use crate::error::{ContextError, ErrorImpl}; use crate::{ContextCompat, Report, StdError, WrapErr}; use core::fmt::{self, Debug, Display, Write}; #[cfg(backtrace)] use std::backtrace::Backtrace; mod ext { use super::*; pub trait StdError { #[cfg_attr(track_caller, track_caller)] fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static; } impl StdError for E where E: std::error::Error + Send + Sync + 'static, { fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static, { Report::from_msg(msg, self) } } impl StdError for Report { fn ext_report(self, msg: D) -> Report where D: Display + Send + Sync + 'static, { self.wrap_err(msg) } } } impl WrapErr for Result where E: ext::StdError + Send + Sync + 'static, { fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { match self { Ok(t) => Ok(t), Err(e) => Err(e.ext_report(msg)), } } fn wrap_err_with(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, { match self { Ok(t) => Ok(t), Err(e) => Err(e.ext_report(msg())), } } fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { self.wrap_err(msg) } fn with_context(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, { self.wrap_err_with(msg) } } impl ContextCompat for Option { fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { self.context(msg) } fn wrap_err_with(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, { self.with_context(msg) } fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static, { match self { Some(t) => Ok(t), None => Err(Report::from_display(msg)), } } fn with_context(self, msg: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D, { match self { Some(t) => Ok(t), None => Err(Report::from_display(msg())), } } } impl Debug for ContextError where D: Display, E: Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Error") .field("msg", &Quoted(&self.msg)) .field("source", &self.error) .finish() } } impl Display for ContextError where D: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.msg, f) } } impl StdError for ContextError where D: Display, E: StdError + 'static, { #[cfg(backtrace)] fn backtrace(&self) -> Option<&Backtrace> { self.error.backtrace() } fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.error) } } impl StdError for ContextError where D: Display, { fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(ErrorImpl::error(self.error.inner.as_ref())) } } struct Quoted(D); impl Debug for Quoted where D: Display, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_char('"')?; Quoted(&mut *formatter).write_fmt(format_args!("{}", self.0))?; formatter.write_char('"')?; Ok(()) } } impl Write for Quoted<&mut fmt::Formatter<'_>> { fn write_str(&mut self, s: &str) -> fmt::Result { Display::fmt(&s.escape_debug(), self.0) } } pub(crate) mod private { use super::*; pub trait Sealed {} impl Sealed for Result where E: ext::StdError {} impl Sealed for Option {} } eyre-0.6.12/src/error/pyo3_compat.rs000064400000000000000000000002571046102023000154050ustar 00000000000000use crate::Report; impl From for pyo3::PyErr { fn from(error: Report) -> Self { pyo3::exceptions::PyRuntimeError::new_err(format!("{:?}", error)) } } eyre-0.6.12/src/error.rs000064400000000000000000000735551046102023000131630ustar 00000000000000use crate::chain::Chain; use crate::ptr::{MutPtr, OwnedPtr, RefPtr}; use crate::EyreHandler; use crate::{Report, StdError}; use core::any::TypeId; use core::fmt::{self, Debug, Display}; use core::mem::{self, ManuallyDrop}; use core::ptr::{self, NonNull}; use core::ops::{Deref, DerefMut}; impl Report { /// Create a new error object from any error type. /// /// The error type must be threadsafe and `'static`, so that the `Report` /// will be as well. /// /// If the error type does not provide a backtrace, a backtrace will be /// created here to ensure that a backtrace exists. #[cfg_attr(track_caller, track_caller)] pub fn new(error: E) -> Self where E: StdError + Send + Sync + 'static, { Report::from_std(error) } /// Create a new error object from a printable error message. /// /// If the argument implements std::error::Error, prefer `Report::new` /// instead which preserves the underlying error's cause chain and /// backtrace. If the argument may or may not implement std::error::Error /// now or in the future, use `eyre!(err)` which handles either way /// correctly. /// /// `Report::msg("...")` is equivalent to `eyre!("...")` but occasionally /// convenient in places where a function is preferable over a macro, such /// as iterator or stream combinators: /// /// ``` /// # mod ffi { /// # pub struct Input; /// # pub struct Output; /// # pub async fn do_some_work(_: Input) -> Result { /// # unimplemented!() /// # } /// # } /// # /// # use ffi::{Input, Output}; /// # /// use eyre::{Report, Result}; /// use futures::stream::{Stream, StreamExt, TryStreamExt}; /// /// async fn demo(stream: S) -> Result> /// where /// S: Stream, /// { /// stream /// .then(ffi::do_some_work) // returns Result /// .map_err(Report::msg) /// .try_collect() /// .await /// } /// ``` #[cfg_attr(track_caller, track_caller)] pub fn msg(message: M) -> Self where M: Display + Debug + Send + Sync + 'static, { Report::from_adhoc(message) } #[cfg_attr(track_caller, track_caller)] /// Creates a new error from an implementor of [`std::error::Error`] pub(crate) fn from_std(error: E) -> Self where E: StdError + Send + Sync + 'static, { let vtable = &ErrorVTable { object_drop: object_drop::, object_ref: object_ref::, object_mut: object_mut::, object_boxed: object_boxed::, object_downcast: object_downcast::, object_downcast_mut: object_downcast_mut::, object_drop_rest: object_drop_front::, }; // Safety: passing vtable that operates on the right type E. let handler = Some(crate::capture_handler(&error)); unsafe { Report::construct(error, vtable, handler) } } #[cfg_attr(track_caller, track_caller)] pub(crate) fn from_adhoc(message: M) -> Self where M: Display + Debug + Send + Sync + 'static, { use crate::wrapper::MessageError; let error: MessageError = MessageError(message); let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, object_mut: object_mut::>, object_boxed: object_boxed::>, object_downcast: object_downcast::, object_downcast_mut: object_downcast_mut::, object_drop_rest: object_drop_front::, }; // Safety: MessageError is repr(transparent) so it is okay for the // vtable to allow casting the MessageError to M. let handler = Some(crate::capture_handler(&error)); unsafe { Report::construct(error, vtable, handler) } } #[cfg_attr(track_caller, track_caller)] pub(crate) fn from_display(message: M) -> Self where M: Display + Send + Sync + 'static, { use crate::wrapper::{DisplayError, NoneError}; let error: DisplayError = DisplayError(message); let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, object_mut: object_mut::>, object_boxed: object_boxed::>, object_downcast: object_downcast::, object_downcast_mut: object_downcast_mut::, object_drop_rest: object_drop_front::, }; // Safety: DisplayError is repr(transparent) so it is okay for the // vtable to allow casting the DisplayError to M. let handler = Some(crate::capture_handler(&NoneError)); unsafe { Report::construct(error, vtable, handler) } } #[cfg_attr(track_caller, track_caller)] pub(crate) fn from_msg(msg: D, error: E) -> Self where D: Display + Send + Sync + 'static, E: StdError + Send + Sync + 'static, { let error: ContextError = ContextError { msg, error }; let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, object_mut: object_mut::>, object_boxed: object_boxed::>, object_downcast: context_downcast::, object_downcast_mut: context_downcast_mut::, object_drop_rest: context_drop_rest::, }; // Safety: passing vtable that operates on the right type. let handler = Some(crate::capture_handler(&error)); unsafe { Report::construct(error, vtable, handler) } } #[cfg_attr(track_caller, track_caller)] pub(crate) fn from_boxed(error: Box) -> Self { use crate::wrapper::BoxedError; let error = BoxedError(error); let handler = Some(crate::capture_handler(&error)); let vtable = &ErrorVTable { object_drop: object_drop::, object_ref: object_ref::, object_mut: object_mut::, object_boxed: object_boxed::, object_downcast: object_downcast::>, object_downcast_mut: object_downcast_mut::>, object_drop_rest: object_drop_front::>, }; // Safety: BoxedError is repr(transparent) so it is okay for the vtable // to allow casting to Box. unsafe { Report::construct(error, vtable, handler) } } // Takes backtrace as argument rather than capturing it here so that the // user sees one fewer layer of wrapping noise in the backtrace. // // Unsafe because the given vtable must have sensible behavior on the error // value of type E. unsafe fn construct( error: E, vtable: &'static ErrorVTable, handler: Option>, ) -> Self where E: StdError + Send + Sync + 'static, { let inner = ErrorImpl { header: ErrorHeader { vtable, handler }, _object: error, }; // Construct a new owned allocation through a raw pointer // // This does not keep the allocation around as a `Box` which would invalidate an // references when moved let ptr = OwnedPtr::>::new(inner); // Safety: the type let ptr = ptr.cast::>(); Report { inner: ptr } } /// Create a new error from an error message to wrap the existing error. /// /// For attaching a higher level error message to a `Result` as it is propagated, the /// [`WrapErr`][crate::WrapErr] extension trait may be more convenient than this function. /// /// The primary reason to use `error.wrap_err(...)` instead of `result.wrap_err(...)` via the /// `WrapErr` trait would be if the message needs to depend on some data held by the underlying /// error: /// /// ``` /// # use std::fmt::{self, Debug, Display}; /// # /// # type T = (); /// # /// # impl std::error::Error for ParseError {} /// # impl Debug for ParseError { /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { /// # unimplemented!() /// # } /// # } /// # impl Display for ParseError { /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { /// # unimplemented!() /// # } /// # } /// # /// use eyre::Result; /// use std::fs::File; /// use std::path::Path; /// /// struct ParseError { /// line: usize, /// column: usize, /// } /// /// fn parse_impl(file: File) -> Result { /// # const IGNORE: &str = stringify! { /// ... /// # }; /// # unimplemented!() /// } /// /// pub fn parse(path: impl AsRef) -> Result { /// let file = File::open(&path)?; /// parse_impl(file).map_err(|error| { /// let message = format!( /// "only the first {} lines of {} are valid", /// error.line, path.as_ref().display(), /// ); /// eyre::Report::new(error).wrap_err(message) /// }) /// } /// ``` pub fn wrap_err(mut self, msg: D) -> Self where D: Display + Send + Sync + 'static, { // Safety: this access a `ErrorImpl` as a valid reference to a `ErrorImpl<()>` // // As the generic is at the end of the struct and the struct is `repr(C)` this reference // will be within bounds of the original pointer, and the field will have the same offset let handler = header_mut(self.inner.as_mut()).handler.take(); let error: ContextError = ContextError { msg, error: self }; let vtable = &ErrorVTable { object_drop: object_drop::>, object_ref: object_ref::>, object_mut: object_mut::>, object_boxed: object_boxed::>, object_downcast: context_chain_downcast::, object_downcast_mut: context_chain_downcast_mut::, object_drop_rest: context_chain_drop_rest::, }; // Safety: passing vtable that operates on the right type. unsafe { Report::construct(error, vtable, handler) } } /// Access the vtable for the current error object. fn vtable(&self) -> &'static ErrorVTable { header(self.inner.as_ref()).vtable } /// An iterator of the chain of source errors contained by this Report. /// /// This iterator will visit every error in the cause chain of this error /// object, beginning with the error that this error object was created /// from. /// /// # Example /// /// ``` /// use eyre::Report; /// use std::io; /// /// pub fn underlying_io_error_kind(error: &Report) -> Option { /// for cause in error.chain() { /// if let Some(io_error) = cause.downcast_ref::() { /// return Some(io_error.kind()); /// } /// } /// None /// } /// ``` pub fn chain(&self) -> Chain<'_> { ErrorImpl::chain(self.inner.as_ref()) } /// The lowest level cause of this error — this error's cause's /// cause's cause etc. /// /// The root cause is the last error in the iterator produced by /// [`chain()`][Report::chain]. pub fn root_cause(&self) -> &(dyn StdError + 'static) { let mut chain = self.chain(); let mut root_cause = chain.next().unwrap(); for cause in chain { root_cause = cause; } root_cause } /// Returns true if `E` is the type held by this error object. /// /// For errors constructed from messages, this method returns true if `E` matches the type of /// the message `D` **or** the type of the error on which the message has been attached. For /// details about the interaction between message and downcasting, [see here]. /// /// [see here]: trait.WrapErr.html#effect-on-downcasting pub fn is(&self) -> bool where E: Display + Debug + Send + Sync + 'static, { self.downcast_ref::().is_some() } /// Attempt to downcast the error object to a concrete type. pub fn downcast(self) -> Result where E: Display + Debug + Send + Sync + 'static, { let target = TypeId::of::(); unsafe { // Use vtable to find NonNull<()> which points to a value of type E // somewhere inside the data structure. let addr = match (self.vtable().object_downcast)(self.inner.as_ref(), target) { Some(addr) => addr, None => return Err(self), }; // Prepare to read E out of the data structure. We'll drop the rest // of the data structure separately so that E is not dropped. let outer = ManuallyDrop::new(self); // Read E from where the vtable found it. let error = ptr::read(addr.cast::().as_ptr()); // Read Box> from self. Can't move it out because // Report has a Drop impl which we want to not run. let inner = ptr::read(&outer.inner); // Drop rest of the data structure outside of E. (outer.vtable().object_drop_rest)(inner, target); Ok(error) } } /// Downcast this error object by reference. /// /// # Example /// /// ``` /// # use eyre::{Report, eyre}; /// # use std::fmt::{self, Display}; /// # use std::task::Poll; /// # /// # #[derive(Debug)] /// # enum DataStoreError { /// # Censored(()), /// # } /// # /// # impl Display for DataStoreError { /// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { /// # unimplemented!() /// # } /// # } /// # /// # impl std::error::Error for DataStoreError {} /// # /// # const REDACTED_CONTENT: () = (); /// # /// # #[cfg(not(feature = "auto-install"))] /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); /// # /// # let error: Report = eyre!("..."); /// # let root_cause = &error; /// # /// # let ret = /// // If the error was caused by redaction, then return a tombstone instead /// // of the content. /// match root_cause.downcast_ref::() { /// Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), /// None => Err(error), /// } /// # ; /// ``` pub fn downcast_ref(&self) -> Option<&E> where E: Display + Debug + Send + Sync + 'static, { let target = TypeId::of::(); unsafe { // Use vtable to find NonNull<()> which points to a value of type E // somewhere inside the data structure. let addr = (self.vtable().object_downcast)(self.inner.as_ref(), target)?; Some(addr.cast::().as_ref()) } } /// Downcast this error object by mutable reference. pub fn downcast_mut(&mut self) -> Option<&mut E> where E: Display + Debug + Send + Sync + 'static, { let target = TypeId::of::(); unsafe { // Use vtable to find NonNull<()> which points to a value of type E // somewhere inside the data structure. let addr = (self.vtable().object_downcast_mut)(self.inner.as_mut(), target)?; Some(addr.cast::().as_mut()) } } /// Get a reference to the Handler for this Report. pub fn handler(&self) -> &dyn EyreHandler { header(self.inner.as_ref()) .handler .as_ref() .unwrap() .as_ref() } /// Get a mutable reference to the Handler for this Report. pub fn handler_mut(&mut self) -> &mut dyn EyreHandler { header_mut(self.inner.as_mut()) .handler .as_mut() .unwrap() .as_mut() } /// Get a reference to the Handler for this Report. #[doc(hidden)] pub fn context(&self) -> &dyn EyreHandler { header(self.inner.as_ref()) .handler .as_ref() .unwrap() .as_ref() } /// Get a mutable reference to the Handler for this Report. #[doc(hidden)] pub fn context_mut(&mut self) -> &mut dyn EyreHandler { header_mut(self.inner.as_mut()) .handler .as_mut() .unwrap() .as_mut() } } impl From for Report where E: StdError + Send + Sync + 'static, { #[cfg_attr(track_caller, track_caller)] fn from(error: E) -> Self { Report::from_std(error) } } impl Deref for Report { type Target = dyn StdError + Send + Sync + 'static; fn deref(&self) -> &Self::Target { ErrorImpl::error(self.inner.as_ref()) } } impl DerefMut for Report { fn deref_mut(&mut self) -> &mut Self::Target { ErrorImpl::error_mut(self.inner.as_mut()) } } impl Display for Report { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { ErrorImpl::display(self.inner.as_ref(), formatter) } } impl Debug for Report { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { ErrorImpl::debug(self.inner.as_ref(), formatter) } } impl Drop for Report { fn drop(&mut self) { unsafe { // Read Box> from self. (self.vtable().object_drop)(self.inner); } } } struct ErrorVTable { object_drop: unsafe fn(OwnedPtr>), object_ref: unsafe fn(RefPtr<'_, ErrorImpl<()>>) -> &(dyn StdError + Send + Sync + 'static), object_mut: unsafe fn(MutPtr<'_, ErrorImpl<()>>) -> &mut (dyn StdError + Send + Sync + 'static), #[allow(clippy::type_complexity)] object_boxed: unsafe fn(OwnedPtr>) -> Box, object_downcast: unsafe fn(RefPtr<'_, ErrorImpl<()>>, TypeId) -> Option>, object_downcast_mut: unsafe fn(MutPtr<'_, ErrorImpl<()>>, TypeId) -> Option>, object_drop_rest: unsafe fn(OwnedPtr>, TypeId), } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_drop(e: OwnedPtr>) { // Cast to a context type and drop the Box allocation. let unerased = unsafe { e.cast::>().into_box() }; drop(unerased); } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_drop_front(e: OwnedPtr>, target: TypeId) { // Drop the fields of ErrorImpl other than E as well as the Box allocation, // without dropping E itself. This is used by downcast after doing a // ptr::read to take ownership of the E. let _ = target; // Note: This must not use `mem::transmute` because it tries to reborrow the `Unique` // contained in `Box`, which must not be done. In practice this probably won't make any // difference by now, but technically it's unsound. // see: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.m let unerased = unsafe { e.cast::>().into_box() }; mem::forget(unerased._object) } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_ref(e: RefPtr<'_, ErrorImpl<()>>) -> &(dyn StdError + Send + Sync + 'static) where E: StdError + Send + Sync + 'static, { // Attach E's native StdError vtable onto a pointer to self._object. &unsafe { e.cast::>().as_ref() }._object } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_mut(e: MutPtr<'_, ErrorImpl<()>>) -> &mut (dyn StdError + Send + Sync + 'static) where E: StdError + Send + Sync + 'static, { // Attach E's native StdError vtable onto a pointer to self._object. &mut unsafe { e.cast::>().into_mut() }._object } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_boxed(e: OwnedPtr>) -> Box where E: StdError + Send + Sync + 'static, { // Attach ErrorImpl's native StdError vtable. The StdError impl is below. unsafe { e.cast::>().into_box() } } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_downcast(e: RefPtr<'_, ErrorImpl<()>>, target: TypeId) -> Option> where E: 'static, { if TypeId::of::() == target { // Caller is looking for an E pointer and e is ErrorImpl, take a // pointer to its E field. let unerased = unsafe { e.cast::>().as_ref() }; Some(NonNull::from(&(unerased._object)).cast::<()>()) } else { None } } /// # Safety /// /// Requires layout of *e to match ErrorImpl. unsafe fn object_downcast_mut( e: MutPtr<'_, ErrorImpl<()>>, target: TypeId, ) -> Option> where E: 'static, { if TypeId::of::() == target { // Caller is looking for an E pointer and e is ErrorImpl, take a // pointer to its E field. let unerased = unsafe { e.cast::>().into_mut() }; Some(NonNull::from(&mut (unerased._object)).cast::<()>()) } else { None } } /// # Safety /// /// Requires layout of *e to match ErrorImpl>. unsafe fn context_downcast( e: RefPtr<'_, ErrorImpl<()>>, target: TypeId, ) -> Option> where D: 'static, E: 'static, { if TypeId::of::() == target { let unerased = unsafe { e.cast::>>().as_ref() }; let addr = NonNull::from(&unerased._object.msg).cast::<()>(); Some(addr) } else if TypeId::of::() == target { let unerased = unsafe { e.cast::>>().as_ref() }; let addr = NonNull::from(&unerased._object.error).cast::<()>(); Some(addr) } else { None } } /// # Safety /// /// Requires layout of *e to match ErrorImpl>. unsafe fn context_downcast_mut( e: MutPtr<'_, ErrorImpl<()>>, target: TypeId, ) -> Option> where D: 'static, E: 'static, { if TypeId::of::() == target { let unerased = unsafe { e.cast::>>().into_mut() }; let addr = NonNull::from(&unerased._object.msg).cast::<()>(); Some(addr) } else if TypeId::of::() == target { let unerased = unsafe { e.cast::>>().into_mut() }; let addr = NonNull::from(&mut unerased._object.error).cast::<()>(); Some(addr) } else { None } } /// # Safety /// /// Requires layout of *e to match ErrorImpl>. unsafe fn context_drop_rest(e: OwnedPtr>, target: TypeId) where D: 'static, E: 'static, { // Called after downcasting by value to either the D or the E and doing a // ptr::read to take ownership of that value. if TypeId::of::() == target { unsafe { e.cast::, E>>>() .into_box() }; } else { debug_assert_eq!(TypeId::of::(), target); unsafe { e.cast::>>>() .into_box() }; } } /// # Safety /// /// Requires layout of *e to match ErrorImpl>. unsafe fn context_chain_downcast( e: RefPtr<'_, ErrorImpl<()>>, target: TypeId, ) -> Option> where D: 'static, { let unerased = unsafe { e.cast::>>().as_ref() }; if TypeId::of::() == target { let addr = NonNull::from(&unerased._object.msg).cast::<()>(); Some(addr) } else { // Recurse down the context chain per the inner error's vtable. let source = &unerased._object.error; unsafe { (source.vtable().object_downcast)(source.inner.as_ref(), target) } } } /// # Safety /// /// Requires layout of *e to match ErrorImpl>. unsafe fn context_chain_downcast_mut( e: MutPtr<'_, ErrorImpl<()>>, target: TypeId, ) -> Option> where D: 'static, { let unerased = unsafe { e.cast::>>().into_mut() }; if TypeId::of::() == target { let addr = NonNull::from(&unerased._object.msg).cast::<()>(); Some(addr) } else { // Recurse down the context chain per the inner error's vtable. let source = &mut unerased._object.error; unsafe { (source.vtable().object_downcast_mut)(source.inner.as_mut(), target) } } } /// # Safety /// /// Requires layout of *e to match ErrorImpl>. unsafe fn context_chain_drop_rest(e: OwnedPtr>, target: TypeId) where D: 'static, { // Called after downcasting by value to either the D or one of the causes // and doing a ptr::read to take ownership of that value. if TypeId::of::() == target { let unerased = unsafe { e.cast::, Report>>>() .into_box() }; // Drop the entire rest of the data structure rooted in the next Report. drop(unerased); } else { unsafe { let unerased = e .cast::>>>() .into_box(); // Read out a ManuallyDrop>> from the next error. let inner = ptr::read(&unerased.as_ref()._object.error.inner); drop(unerased); // Recursively drop the next error using the same target typeid. (header(inner.as_ref()).vtable.object_drop_rest)(inner, target); } } } #[repr(C)] pub(crate) struct ErrorHeader { vtable: &'static ErrorVTable, pub(crate) handler: Option>, } // repr C to ensure that E remains in the final position. #[repr(C)] pub(crate) struct ErrorImpl { header: ErrorHeader, // NOTE: Don't use directly. Use only through vtable. Erased type may have // different alignment. _object: E, } // repr C to ensure that ContextError has the same layout as // ContextError, E> and ContextError>. #[repr(C)] pub(crate) struct ContextError { pub(crate) msg: D, pub(crate) error: E, } impl ErrorImpl { /// Returns a type erased Error fn erase(&self) -> RefPtr<'_, ErrorImpl<()>> { // Erase the concrete type of E but preserve the vtable in self.vtable // for manipulating the resulting thin pointer. This is analogous to an // unsize coersion. RefPtr::new(self).cast() } } // Reads the header out of `p`. This is the same as `p.as_ref().header`, but // avoids converting `p` into a reference of a shrunk provenance with a type different than the // allocation. fn header(p: RefPtr<'_, ErrorImpl<()>>) -> &'_ ErrorHeader { // Safety: `ErrorHeader` is the first field of repr(C) `ErrorImpl` unsafe { p.cast().as_ref() } } fn header_mut(p: MutPtr<'_, ErrorImpl<()>>) -> &mut ErrorHeader { // Safety: `ErrorHeader` is the first field of repr(C) `ErrorImpl` unsafe { p.cast().into_mut() } } impl ErrorImpl<()> { pub(crate) fn error(this: RefPtr<'_, Self>) -> &(dyn StdError + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right // original type E. unsafe { (header(this).vtable.object_ref)(this) } } pub(crate) fn error_mut(this: MutPtr<'_, Self>) -> &mut (dyn StdError + Send + Sync + 'static) { // Use vtable to attach E's native StdError vtable for the right // original type E. unsafe { (header_mut(this).vtable.object_mut)(this) } } pub(crate) fn chain(this: RefPtr<'_, Self>) -> Chain<'_> { Chain::new(Self::error(this)) } pub(crate) fn header(this: RefPtr<'_, ErrorImpl>) -> &ErrorHeader { header(this) } } impl StdError for ErrorImpl where E: StdError, { fn source(&self) -> Option<&(dyn StdError + 'static)> { ErrorImpl::<()>::error(self.erase()).source() } } impl Debug for ErrorImpl where E: Debug, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { ErrorImpl::debug(self.erase(), formatter) } } impl Display for ErrorImpl where E: Display, { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(ErrorImpl::error(self.erase()), formatter) } } impl From for Box { fn from(error: Report) -> Self { let outer = ManuallyDrop::new(error); unsafe { // Read Box> from error. Can't move it out because // Report has a Drop impl which we want to not run. // Use vtable to attach ErrorImpl's native StdError vtable for // the right original type E. (header(outer.inner.as_ref()).vtable.object_boxed)(outer.inner) } } } impl From for Box { fn from(error: Report) -> Self { Box::::from(error) } } impl AsRef for Report { fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { &**self } } impl AsRef for Report { fn as_ref(&self) -> &(dyn StdError + 'static) { &**self } } #[cfg(feature = "pyo3")] mod pyo3_compat; eyre-0.6.12/src/fmt.rs000064400000000000000000000014111046102023000125760ustar 00000000000000use crate::{error::ErrorImpl, ptr::RefPtr}; use core::fmt; impl ErrorImpl<()> { pub(crate) fn display(this: RefPtr<'_, Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { ErrorImpl::header(this) .handler .as_ref() .map(|handler| handler.display(Self::error(this), f)) .unwrap_or_else(|| core::fmt::Display::fmt(Self::error(this), f)) } /// Debug formats the error using the captured handler pub(crate) fn debug(this: RefPtr<'_, Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result { ErrorImpl::header(this) .handler .as_ref() .map(|handler| handler.debug(Self::error(this), f)) .unwrap_or_else(|| core::fmt::Debug::fmt(Self::error(this), f)) } } eyre-0.6.12/src/kind.rs000064400000000000000000000054601046102023000127450ustar 00000000000000#![allow(missing_debug_implementations, missing_docs)] // Tagged dispatch mechanism for resolving the behavior of `eyre!($expr)`. // // When eyre! is given a single expr argument to turn into eyre::Report, we // want the resulting Report to pick up the input's implementation of source() // and backtrace() if it has a std::error::Error impl, otherwise require nothing // more than Display and Debug. // // Expressed in terms of specialization, we want something like: // // trait EyreNew { // fn new(self) -> Report; // } // // impl EyreNew for T // where // T: Display + Debug + Send + Sync + 'static, // { // default fn new(self) -> Report { // /* no std error impl */ // } // } // // impl EyreNew for T // where // T: std::error::Error + Send + Sync + 'static, // { // fn new(self) -> Report { // /* use std error's source() and backtrace() */ // } // } // // Since specialization is not stable yet, instead we rely on autoref behavior // of method resolution to perform tagged dispatch. Here we have two traits // AdhocKind and TraitKind that both have an eyre_kind() method. AdhocKind is // implemented whether or not the caller's type has a std error impl, while // TraitKind is implemented only when a std error impl does exist. The ambiguity // is resolved by AdhocKind requiring an extra autoref so that it has lower // precedence. // // The eyre! macro will set up the call in this form: // // #[allow(unused_imports)] // use $crate::private::{AdhocKind, TraitKind}; // let error = $msg; // (&error).eyre_kind().new(error) use crate::Report; use core::fmt::{Debug, Display}; use crate::StdError; pub struct Adhoc; pub trait AdhocKind: Sized { #[inline] fn eyre_kind(&self) -> Adhoc { Adhoc } } impl AdhocKind for &T where T: ?Sized + Display + Debug + Send + Sync + 'static {} impl Adhoc { #[cfg_attr(track_caller, track_caller)] pub fn new(self, message: M) -> Report where M: Display + Debug + Send + Sync + 'static, { Report::from_adhoc(message) } } pub struct Trait; pub trait TraitKind: Sized { #[inline] fn eyre_kind(&self) -> Trait { Trait } } impl TraitKind for E where E: Into {} impl Trait { #[cfg_attr(track_caller, track_caller)] pub fn new(self, error: E) -> Report where E: Into, { error.into() } } pub struct Boxed; pub trait BoxedKind: Sized { #[inline] fn eyre_kind(&self) -> Boxed { Boxed } } impl BoxedKind for Box {} impl Boxed { #[cfg_attr(track_caller, track_caller)] pub fn new(self, error: Box) -> Report { Report::from_boxed(error) } } eyre-0.6.12/src/lib.rs000064400000000000000000001257141046102023000125730ustar 00000000000000//! This library provides [`eyre::Report`][Report], a trait object based //! error handling type for easy idiomatic error handling and reporting in Rust //! applications. //! //! This crate is a fork of [`anyhow`] with support for customized //! error reports. For more details on customization, check out the docs on //! [`eyre::EyreHandler`]. //! //! ## Custom Report Handlers //! //! The heart of this crate is its ability to swap out the Handler type to change //! what information is carried alongside errors and how the end report is //! formatted. This crate is meant to be used alongside companion crates that //! customize its behavior. Below is a list of known crates that export report //! handlers for eyre and short summaries of what features they provide. //! //! - [`stable-eyre`]: Switches the backtrace type from `std`'s to `backtrace-rs`'s //! so that it can be captured on stable. The report format is identical to //! `DefaultHandler`'s report format. //! - [`color-eyre`]: Captures a `backtrace::Backtrace` and a //! `tracing_error::SpanTrace`. Provides a `Section` trait for attaching warnings //! and suggestions to error reports. The end report is then pretty printed with //! the help of [`color-backtrace`], [`color-spantrace`], and `ansi_term`. Check //! out the README on [`color-eyre`] for details on the report format. //! - [`simple-eyre`]: A minimal `EyreHandler` that captures no additional //! information, for when you do not wish to capture `Backtrace`s with errors. //! - [`jane-eyre`]: A report handler crate that exists purely for the pun. //! Currently just re-exports `color-eyre`. //! //! ## Usage Recommendations and Stability Considerations //! //! **We recommend users do not re-export types from this library as part their //! own public API for libraries with external users.** The main reason for this //! is that it will make your library API break if we ever bump the major version //! number on eyre and your users upgrade the eyre version they use in their //! application code before you upgrade your own eyre dep version[^1]. //! //! However, even beyond this API stability hazard, there are other good reasons //! to avoid using `eyre::Report` as your public error type. //! //! - You export an undocumented error interface that is otherwise still //! accessible via downcast, making it hard for users to react to specific //! errors while not preventing them from depending on details you didn't mean //! to make part of your public API. //! - This in turn makes the error types of all libraries you use a part of //! your public API as well, and makes changing any of those libraries into //! undetectable runtime breakage. //! - If many of your errors are constructed from strings, you encourage your //! users to use string comparison for reacting to specific errors, which is //! brittle and turns updating error messages into potentially undetectable //! runtime breakage. //! //! ## Details //! //! - Use `Result`, or equivalently `eyre::Result`, as the //! return type of any fallible function. //! //! Within the function, use `?` to easily propagate any error that implements the //! `std::error::Error` trait. //! //! ```rust //! # pub trait Deserialize {} //! # //! # mod serde_json { //! # use super::Deserialize; //! # use std::io; //! # //! # pub fn from_str(json: &str) -> io::Result { //! # unimplemented!() //! # } //! # } //! # //! # struct ClusterMap; //! # //! # impl Deserialize for ClusterMap {} //! # //! use eyre::Result; //! //! fn get_cluster_info() -> Result { //! let config = std::fs::read_to_string("cluster.json")?; //! let map: ClusterMap = serde_json::from_str(&config)?; //! Ok(map) //! } //! # //! # fn main() {} //! ``` //! //! - Wrap a lower level error with a new error created from a message to help the //! person troubleshooting understand the chain of failures that occurred. A //! low-level error like "No such file or directory" can be annoying to debug //! without more information about what higher level step the application was in //! the middle of. //! //! ```rust //! # struct It; //! # //! # impl It { //! # fn detach(&self) -> Result<()> { //! # unimplemented!() //! # } //! # } //! # //! use eyre::{WrapErr, Result}; //! //! fn main() -> Result<()> { //! # return Ok(()); //! # //! # const _: &str = stringify! { //! ... //! # }; //! # //! # let it = It; //! # let path = "./path/to/instrs.json"; //! # //! it.detach().wrap_err("Failed to detach the important thing")?; //! //! let content = std::fs::read(path) //! .wrap_err_with(|| format!("Failed to read instrs from {}", path))?; //! # //! # const _: &str = stringify! { //! ... //! # }; //! # //! # Ok(()) //! } //! ``` //! //! ```console //! Error: Failed to read instrs from ./path/to/instrs.json //! //! Caused by: //! No such file or directory (os error 2) //! ``` //! //! - Downcasting is supported and can be done by value, by shared reference, or by //! mutable reference as needed. //! //! ```rust //! # use eyre::{Report, eyre}; //! # use std::fmt::{self, Display}; //! # use std::task::Poll; //! # //! # #[derive(Debug)] //! # enum DataStoreError { //! # Censored(()), //! # } //! # //! # impl Display for DataStoreError { //! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { //! # unimplemented!() //! # } //! # } //! # //! # impl std::error::Error for DataStoreError {} //! # //! # const REDACTED_CONTENT: () = (); //! # //! # #[cfg(not(feature = "auto-install"))] //! # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); //! # //! # let error: Report = eyre!("..."); //! # let root_cause = &error; //! # //! # let ret = //! // If the error was caused by redaction, then return a //! // tombstone instead of the content. //! match root_cause.downcast_ref::() { //! Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)), //! None => Err(error), //! } //! # ; //! ``` //! //! - If using the nightly channel, a backtrace is captured and printed with the //! error if the underlying error type does not already provide its own. In order //! to see backtraces, they must be enabled through the environment variables //! described in [`std::backtrace`]: //! //! - If you want panics and errors to both have backtraces, set //! `RUST_BACKTRACE=1`; //! - If you want only errors to have backtraces, set `RUST_LIB_BACKTRACE=1`; //! - If you want only panics to have backtraces, set `RUST_BACKTRACE=1` and //! `RUST_LIB_BACKTRACE=0`. //! //! The tracking issue for this feature is [rust-lang/rust#53487]. //! //! [`std::backtrace`]: https://doc.rust-lang.org/std/backtrace/index.html#environment-variables //! [rust-lang/rust#53487]: https://github.com/rust-lang/rust/issues/53487 //! //! - Eyre works with any error type that has an impl of `std::error::Error`, //! including ones defined in your crate. We do not bundle a `derive(Error)` macro //! but you can write the impls yourself or use a standalone macro like //! [thiserror]. //! //! ```rust //! use thiserror::Error; //! //! #[derive(Error, Debug)] //! pub enum FormatError { //! #[error("Invalid header (expected {expected:?}, got {found:?})")] //! InvalidHeader { //! expected: String, //! found: String, //! }, //! #[error("Missing attribute: {0}")] //! MissingAttribute(String), //! } //! ``` //! //! - One-off error messages can be constructed using the `eyre!` macro, which //! supports string interpolation and produces an `eyre::Report`. //! //! ```rust //! # use eyre::{eyre, Result}; //! # //! # fn demo() -> Result<()> { //! # let missing = "..."; //! return Err(eyre!("Missing attribute: {}", missing)); //! # Ok(()) //! # } //! ``` //! //! - On newer versions of the compiler (i.e. 1.58 and later) this macro also //! supports format args captures. //! //! ```rust //! # use eyre::{eyre, Result}; //! # //! # fn demo() -> Result<()> { //! # let missing = "..."; //! # #[cfg(not(eyre_no_fmt_args_capture))] //! return Err(eyre!("Missing attribute: {missing}")); //! # Ok(()) //! # } //! ``` //! //! ## No-std support //! //! No-std support was removed in 2020 in [commit 608a16a] due to unaddressed upstream breakages. //! //! [commit 608a16a]: https://github.com/eyre-rs/eyre/pull/29/commits/608a16aa2c2c27eca6c88001cc94c6973c18f1d5 //! //! ## Comparison to failure //! //! The `eyre::Report` type works something like `failure::Error`, but unlike //! failure ours is built around the standard library's `std::error::Error` trait //! rather than a separate trait `failure::Fail`. The standard library has adopted //! the necessary improvements for this to be possible as part of [RFC 2504]. //! //! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md //! //! ## Comparison to thiserror //! //! Use `eyre` if you don't think you'll do anything with an error other than //! report it. This is common in application code. Use `thiserror` if you think //! you need an error type that can be handled via match or reported. This is //! common in library crates where you don't know how your users will handle //! your errors. //! //! [thiserror]: https://github.com/dtolnay/thiserror //! //! ## Compatibility with `anyhow` //! //! This crate does its best to be usable as a drop in replacement of `anyhow` and //! vice-versa by re-exporting all of the renamed APIs with the names used in //! `anyhow`, though there are some differences still. //! //! #### `Context` and `Option` //! //! As part of renaming `Context` to `WrapErr` we also intentionally do not //! implement `WrapErr` for `Option`. This decision was made because `wrap_err` //! implies that you're creating a new error that saves the old error as its //! `source`. With `Option` there is no source error to wrap, so `wrap_err` ends up //! being somewhat meaningless. //! //! Instead `eyre` offers [`OptionExt::ok_or_eyre`] to yield _static_ errors from `None`, //! and intends for users to use the combinator functions provided by //! `std`, converting `Option`s to `Result`s, for _dynamic_ errors. //! So where you would write this with //! anyhow: //! //! ```rust //! use anyhow::Context; //! //! let opt: Option<()> = None; //! let result_static = opt.context("static error message"); //! let result_dynamic = opt.with_context(|| format!("{} error message", "dynamic")); //! ``` //! //! With `eyre` we want users to write: //! //! ```rust //! use eyre::{eyre, OptionExt, Result}; //! //! # #[cfg(not(feature = "auto-install"))] //! # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); //! # //! let opt: Option<()> = None; //! let result_static: Result<()> = opt.ok_or_eyre("static error message"); //! let result_dynamic: Result<()> = opt.ok_or_else(|| eyre!("{} error message", "dynamic")); //! ``` //! //! **NOTE**: However, to help with porting we do provide a `ContextCompat` trait which //! implements `context` for options which you can import to make existing //! `.context` calls compile. //! //! [^1]: example and explanation of breakage //! //! [Report]: https://docs.rs/eyre/*/eyre/struct.Report.html //! [`eyre::EyreHandler`]: https://docs.rs/eyre/*/eyre/trait.EyreHandler.html //! [`eyre::WrapErr`]: https://docs.rs/eyre/*/eyre/trait.WrapErr.html //! [`anyhow::Context`]: https://docs.rs/anyhow/*/anyhow/trait.Context.html //! [`anyhow`]: https://github.com/dtolnay/anyhow //! [`tracing_error::SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html //! [`stable-eyre`]: https://github.com/eyre-rs/stable-eyre //! [`color-eyre`]: https://github.com/eyre-rs/color-eyre //! [`jane-eyre`]: https://github.com/yaahc/jane-eyre //! [`simple-eyre`]: https://github.com/eyre-rs/simple-eyre //! [`color-spantrace`]: https://github.com/eyre-rs/color-spantrace //! [`color-backtrace`]: https://github.com/athre0z/color-backtrace #![doc(html_root_url = "https://docs.rs/eyre/0.6.12")] #![cfg_attr( nightly, feature(rustdoc_missing_doc_code_examples), warn(rustdoc::missing_doc_code_examples) )] #![warn( missing_debug_implementations, missing_docs, unsafe_op_in_unsafe_fn, rust_2018_idioms, unreachable_pub, bad_style, dead_code, improper_ctypes, non_shorthand_field_patterns, no_mangle_generic_items, overflowing_literals, path_statements, patterns_in_fns_without_body, unconditional_recursion, unused, unused_allocation, unused_comparisons, unused_parens, while_true )] #![cfg_attr(backtrace, feature(backtrace))] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![allow( clippy::needless_doctest_main, clippy::new_ret_no_self, clippy::wrong_self_convention )] extern crate alloc; #[macro_use] mod backtrace; mod chain; mod context; mod error; mod fmt; mod kind; mod macros; mod option; mod ptr; mod wrapper; use crate::backtrace::Backtrace; use crate::error::ErrorImpl; use core::fmt::{Debug, Display}; use std::error::Error as StdError; pub use eyre as format_err; /// Compatibility re-export of `eyre` for interop with `anyhow` pub use eyre as anyhow; use once_cell::sync::OnceCell; use ptr::OwnedPtr; #[doc(hidden)] pub use DefaultHandler as DefaultContext; #[doc(hidden)] pub use EyreHandler as EyreContext; #[doc(hidden)] pub use Report as ErrReport; /// Compatibility re-export of `Report` for interop with `anyhow` pub use Report as Error; /// Compatibility re-export of `WrapErr` for interop with `anyhow` pub use WrapErr as Context; /// The core error reporting type of the library, a wrapper around a dynamic error reporting type. /// /// `Report` works a lot like `Box`, but with these /// differences: /// /// - `Report` requires that the error is `Send`, `Sync`, and `'static`. /// - `Report` guarantees that a backtrace is available, even if the underlying /// error type does not provide one. /// - `Report` is represented as a narrow pointer — exactly one word in /// size instead of two. /// /// # Display representations /// /// When you print an error object using "{}" or to_string(), only the outermost underlying error /// is printed, not any of the lower level causes. This is exactly as if you had called the Display /// impl of the error from which you constructed your eyre::Report. /// /// ```console /// Failed to read instrs from ./path/to/instrs.json /// ``` /// /// To print causes as well using eyre's default formatting of causes, use the /// alternate selector "{:#}". /// /// ```console /// Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2) /// ``` /// /// The Debug format "{:?}" includes your backtrace if one was captured. Note /// that this is the representation you get by default if you return an error /// from `fn main` instead of printing it explicitly yourself. /// /// ```console /// Error: Failed to read instrs from ./path/to/instrs.json /// /// Caused by: /// No such file or directory (os error 2) /// /// Stack backtrace: /// 0: ::ext_report /// at /git/eyre/src/backtrace.rs:26 /// 1: core::result::Result::map_err /// at /git/rustc/src/libcore/result.rs:596 /// 2: eyre::context:: for core::result::Result>::wrap_err_with /// at /git/eyre/src/context.rs:58 /// 3: testing::main /// at src/main.rs:5 /// 4: std::rt::lang_start /// at /git/rustc/src/libstd/rt.rs:61 /// 5: main /// 6: __libc_start_main /// 7: _start /// ``` /// /// To see a conventional struct-style Debug representation, use "{:#?}". /// /// ```console /// Error { /// msg: "Failed to read instrs from ./path/to/instrs.json", /// source: Os { /// code: 2, /// kind: NotFound, /// message: "No such file or directory", /// }, /// } /// ``` /// /// If none of the built-in representations are appropriate and you would prefer /// to render the error and its cause chain yourself, it can be done by defining /// your own [`EyreHandler`] and [`hook`] to use it. /// /// [`EyreHandler`]: trait.EyreHandler.html /// [`hook`]: fn.set_hook.html #[must_use] pub struct Report { inner: OwnedPtr>, } 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, Clone, Copy)] 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 {} /// Install the provided error hook for constructing EyreHandlers when converting /// Errors to Reports /// /// # Details /// /// To customize the format and content of error reports from `eyre` you must /// first define a new `EyreHandler` type to capture and store the extra context /// and to define the format of how to display the chain of errors and this /// stored context. Once this type has been defined you must also define a global /// hook used to construct these handlers whenever `Report`s are constructed. /// /// # Examples /// /// ```rust,should_panic /// use backtrace::Backtrace; /// use eyre::EyreHandler; /// use std::error::Error; /// use std::{fmt, iter}; /// /// fn main() -> eyre::Result<()> { /// // Install our custom eyre report hook for constructing our custom Handlers /// install().unwrap(); /// /// // construct a report with, hopefully, our custom handler! /// let mut report = eyre::eyre!("hello from custom error town!"); /// /// // manually set the custom msg for this report after it has been constructed /// if let Some(handler) = report.handler_mut().downcast_mut::() { /// handler.custom_msg = Some("you're the best users, you know that right???"); /// } /// /// // print that shit!! /// Err(report) /// } /// /// // define a handler that captures backtraces unless told not to /// fn install() -> Result<(), impl Error> { /// let capture_backtrace = std::env::var("RUST_BACKWARDS_TRACE") /// .map(|val| val != "0") /// .unwrap_or(true); /// /// let hook = Hook { capture_backtrace }; /// /// eyre::set_hook(Box::new(move |e| Box::new(hook.make_handler(e)))) /// } /// /// struct Hook { /// capture_backtrace: bool, /// } /// /// impl Hook { /// fn make_handler(&self, _error: &(dyn Error + 'static)) -> Handler { /// let backtrace = if self.capture_backtrace { /// Some(Backtrace::new()) /// } else { /// None /// }; /// /// Handler { /// backtrace, /// custom_msg: None, /// } /// } /// } /// /// struct Handler { /// // custom configured backtrace capture /// backtrace: Option, /// // customizable message payload associated with reports /// custom_msg: Option<&'static str>, /// } /// /// impl EyreHandler for Handler { /// fn debug(&self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result { /// if f.alternate() { /// return fmt::Debug::fmt(error, f); /// } /// /// let errors = iter::successors(Some(error), |error| (*error).source()); /// /// for (ind, error) in errors.enumerate() { /// write!(f, "\n{:>4}: {}", ind, error)?; /// } /// /// if let Some(backtrace) = self.backtrace.as_ref() { /// writeln!(f, "\n\nBacktrace:\n{:?}", backtrace)?; /// } /// /// if let Some(msg) = self.custom_msg.as_ref() { /// writeln!(f, "\n\n{}", msg)?; /// } /// /// Ok(()) /// } /// } /// ``` 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 StdError + 'static)) -> Box { #[cfg(not(feature = "auto-install"))] let hook = HOOK .get() .expect("a handler must always be installed if the `auto-install` feature is disabled") .as_ref(); #[cfg(feature = "auto-install")] let hook = HOOK .get_or_init(|| Box::new(DefaultHandler::default_with)) .as_ref(); let mut handler = hook(error); #[cfg(track_caller)] { handler.track_caller(std::panic::Location::caller()) } handler } impl dyn EyreHandler { /// 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 EyreHandler as *const T)) } } else { None } } /// pub fn downcast_mut(&mut self) -> Option<&mut T> { if self.is::() { unsafe { Some(&mut *(self as *mut dyn EyreHandler as *mut T)) } } else { None } } } /// Error Report Handler trait for customizing `eyre::Report` pub trait EyreHandler: core::any::Any + Send + Sync { /// Define the report format /// /// Used to override the report format of `eyre::Report` /// /// # Example /// /// ```rust /// use backtrace::Backtrace; /// use eyre::EyreHandler; /// use eyre::Chain; /// use std::error::Error; /// use indenter::indented; /// /// pub struct Handler { /// backtrace: Backtrace, /// } /// /// impl EyreHandler for Handler { /// fn debug( /// &self, /// error: &(dyn Error + 'static), /// 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)?; /// /// if let Some(cause) = error.source() { /// write!(f, "\n\nCaused by:")?; /// let multiple = cause.source().is_some(); /// /// for (n, error) in Chain::new(cause).enumerate() { /// writeln!(f)?; /// if multiple { /// write!(indented(f).ind(n), "{}", error)?; /// } else { /// write!(indented(f), "{}", error)?; /// } /// } /// } /// /// let backtrace = &self.backtrace; /// write!(f, "\n\nStack backtrace:\n{:?}", backtrace)?; /// /// Ok(()) /// } /// } /// ``` fn debug( &self, error: &(dyn StdError + 'static), 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)?; } } Result::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>) {} } /// The default provided error report handler for `eyre::Report`. /// /// On nightly this supports conditionally capturing a `std::backtrace::Backtrace` if the source /// error did not already capture one. #[allow(dead_code)] pub struct DefaultHandler { backtrace: Option, #[cfg(track_caller)] location: Option<&'static std::panic::Location<'static>>, } impl DefaultHandler { /// Manual hook which constructs `DefaultHandler`s. /// /// # Details /// /// When supplied to the `set_hook` function, `default_with` will cause `eyre::Report` to use /// `DefaultHandler` as the error report handler. /// /// If the `auto-install` feature is enabled, and a user-provided hook for constructing /// `EyreHandlers` was not installed using `set_hook`, `DefaultHandler::default_with` /// is automatically installed as the hook. /// /// # Example /// /// ```rust,should_panic /// use eyre::{DefaultHandler, eyre, InstallError, Result, set_hook}; /// /// fn main() -> Result<()> { /// install_default().expect("default handler inexplicably already installed"); /// Err(eyre!("hello from default error city!")) /// } /// /// fn install_default() -> Result<(), InstallError> { /// set_hook(Box::new(DefaultHandler::default_with)) /// } /// /// ``` #[allow(unused_variables)] #[cfg_attr(not(feature = "auto-install"), allow(dead_code))] pub fn default_with(error: &(dyn StdError + 'static)) -> Box { let backtrace = backtrace_if_absent!(error); Box::new(Self { backtrace, #[cfg(track_caller)] location: None, }) } } impl core::fmt::Debug for DefaultHandler { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("DefaultHandler") .field( "backtrace", match &self.backtrace { Some(_) => &"Some(Backtrace { ... })", None => &"None", }, ) .finish() } } impl EyreHandler for DefaultHandler { fn debug( &self, error: &(dyn StdError + 'static), 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)?; if let Some(cause) = error.source() { write!(f, "\n\nCaused by:")?; let multiple = cause.source().is_some(); for (n, error) in crate::chain::Chain::new(cause).enumerate() { writeln!(f)?; if multiple { write!(indenter::indented(f).ind(n), "{}", error)?; } else { write!(indenter::indented(f), "{}", error)?; } } } #[cfg(all(track_caller, feature = "track-caller"))] { if let Some(location) = self.location { write!(f, "\n\nLocation:\n")?; write!(indenter::indented(f), "{}", location)?; } } #[cfg(backtrace)] { use std::backtrace::BacktraceStatus; let backtrace = self .backtrace .as_ref() .or_else(|| error.backtrace()) .expect("backtrace capture failed"); if let BacktraceStatus::Captured = backtrace.status() { write!(f, "\n\nStack backtrace:\n{}", backtrace)?; } } Result::Ok(()) } #[cfg(track_caller)] fn track_caller(&mut self, location: &'static std::panic::Location<'static>) { self.location = Some(location); } } /// Iterator of a chain of source errors. /// /// This type is the iterator returned by [`Report::chain`]. /// /// # Example /// /// ``` /// use eyre::Report; /// use std::io; /// /// pub fn underlying_io_error_kind(error: &Report) -> Option { /// for cause in error.chain() { /// if let Some(io_error) = cause.downcast_ref::() { /// return Some(io_error.kind()); /// } /// } /// None /// } /// ``` #[derive(Clone)] #[allow(missing_debug_implementations)] pub struct Chain<'a> { state: crate::chain::ChainState<'a>, } /// type alias for `Result` /// /// This is a reasonable return type to use throughout your application but also for `fn main`; if /// you do, failures will be printed along with a backtrace if one was captured. /// /// `eyre::Result` may be used with one *or* two type parameters. /// /// ```rust /// use eyre::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 eyre::Result; /// /// fn main() -> Result<()> { /// # return Ok(()); /// let config = std::fs::read_to_string("cluster.json")?; /// let map: ClusterMap = serde_json::from_str(&config)?; /// println!("cluster info: {:#?}", map); /// Ok(()) /// } /// ``` pub type Result = core::result::Result; /// Provides the `wrap_err` method for `Result`. /// /// This trait is sealed and cannot be implemented for types outside of /// `eyre`. /// /// # Example /// /// ``` /// use eyre::{WrapErr, Result}; /// use std::fs; /// use std::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) /// .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 Don't impl `Error` (e.g. `&str` and `Box`) /// /// Due to restrictions for coherence `Report` cannot impl `From` for types that don't impl /// `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 eyre::{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 std::error::Error; /// use eyre::{WrapErr, Report, eyre}; /// /// fn wrap_example(err: Result<(), Box>) -> Result<(), Report> { /// err.map_err(|e| eyre!(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 /// `eyre::Report` may be downcast to `D` **or** to `E`. /// /// That is, in codebases that rely on downcasting, Eyre'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 Eyre, 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 eyre::bail; /// # use thiserror::Error; /// # /// # #[derive(Error, Debug)] /// # #[error("???")] /// # struct SuspiciousError; /// # /// # fn helper() -> Result<()> { /// # bail!(SuspiciousError); /// # } /// # /// use eyre::{WrapErr, Result}; /// /// fn do_it() -> Result<()> { /// helper().wrap_err("Failed to complete the work")?; /// # const IGNORE: &str = stringify! { /// ... /// # }; /// # unreachable!() /// } /// /// fn main() { /// # #[cfg(not(feature = "auto-install"))] /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); /// 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 eyre::bail; /// # use thiserror::Error; /// # /// # #[derive(Error, Debug)] /// # #[error("???")] /// # struct HelperFailed; /// # /// # fn helper() -> Result<()> { /// # bail!("no such file or directory"); /// # } /// # /// use eyre::{WrapErr, Result}; /// /// fn do_it() -> Result<()> { /// helper().wrap_err(HelperFailed)?; /// # const IGNORE: &str = stringify! { /// ... /// # }; /// # unreachable!() /// } /// /// fn main() { /// # #[cfg(not(feature = "auto-install"))] /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); /// 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"); /// } /// ``` /// /// # `wrap_err` vs `wrap_err_with` /// /// `wrap_err` incurs a runtime cost even in the non-error case because it requires eagerly /// constructing the error object. `wrap_err_with` avoids this cost through lazy evaluation. This /// cost is proportional to the cost of the currently installed [`EyreHandler`]'s creation step. /// `wrap_err` is useful in cases where an constructed error object already exists. 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; } /// Provides the [`ok_or_eyre`][OptionExt::ok_or_eyre] method for [`Option`]. /// /// This trait is sealed and cannot be implemented for types outside of /// `eyre`. /// /// # Example /// /// ``` /// # #[cfg(not(feature = "auto-install"))] /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); /// use eyre::OptionExt; /// /// let option: Option<()> = None; /// /// let result = option.ok_or_eyre("static str error"); /// /// assert_eq!(result.unwrap_err().to_string(), "static str error"); /// ``` /// /// # `ok_or_eyre` vs `ok_or_else` /// /// If string interpolation is required for the generated [report][Report], /// use [`ok_or_else`][Option::ok_or_else] instead, /// invoking [`eyre!`] to perform string interpolation: /// /// ``` /// # #[cfg(not(feature = "auto-install"))] /// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap(); /// use eyre::eyre; /// /// let option: Option<()> = None; /// /// let result = option.ok_or_else(|| eyre!("{} error", "dynamic")); /// /// assert_eq!(result.unwrap_err().to_string(), "dynamic error"); /// ``` /// /// `ok_or_eyre` incurs no runtime cost, as the error object /// is constructed from the provided static argument /// only in the `None` case. pub trait OptionExt: context::private::Sealed { /// Transform the [`Option`] into a [`Result`], /// mapping [`Some(v)`][Option::Some] to [`Ok(v)`][Result::Ok] /// and [`None`] to [`Report`]. /// /// `ok_or_eyre` allows for eyre [`Report`] error objects /// to be lazily created from static messages in the `None` case. /// /// For dynamic error messages, use [`ok_or_else`][Option::ok_or_else], /// invoking [`eyre!`] in the closure to perform string interpolation. fn ok_or_eyre(self, message: M) -> crate::Result where M: Debug + Display + Send + Sync + 'static; } /// Provides the `context` method for `Option` when porting from `anyhow` /// /// This trait is sealed and cannot be implemented for types outside of /// `eyre`. /// /// ## Why Doesn't `Eyre` impl `WrapErr` for `Option`? /// /// `eyre` doesn't impl `WrapErr` for `Option` because `wrap_err` implies that you're creating a /// new error that saves the previous error as its `source`. Calling `wrap_err` on an `Option` is /// meaningless because there is no source error. `anyhow` avoids this issue by using a different /// mental model where you're adding "context" to an error, though this not a mental model for /// error handling that `eyre` agrees with. /// /// Instead, `eyre` encourages users to think of each error as distinct, where the previous error /// is the context being saved by the new error, which is backwards compared to anyhow's model. In /// this model you're encouraged to use combinators provided by `std` for `Option` to convert an /// option to a `Result` /// /// # Example /// /// Instead of: /// /// ```rust /// use eyre::ContextCompat; /// /// fn get_thing(mut things: impl Iterator) -> eyre::Result { /// things /// .find(|&thing| thing == 42) /// .context("the thing wasnt in the list") /// } /// ``` /// /// We encourage you to use this: /// /// ```rust /// use eyre::eyre; /// /// fn get_thing(mut things: impl Iterator) -> eyre::Result { /// things /// .find(|&thing| thing == 42) /// .ok_or_else(|| eyre!("the thing wasnt in the list")) /// } /// ``` pub trait ContextCompat: context::private::Sealed { /// Compatibility version of `wrap_err` for creating new errors with new source on `Option` /// when porting from `anyhow` #[cfg_attr(track_caller, track_caller)] fn context(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Compatibility version of `wrap_err_with` for creating new errors with new source on `Option` /// when porting from `anyhow` #[cfg_attr(track_caller, track_caller)] fn with_context(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; /// Compatibility re-export of `context` for porting from `anyhow` to `eyre` #[cfg_attr(track_caller, track_caller)] fn wrap_err(self, msg: D) -> Result where D: Display + Send + Sync + 'static; /// Compatibility re-export of `with_context` for porting from `anyhow` to `eyre` #[cfg_attr(track_caller, track_caller)] fn wrap_err_with(self, f: F) -> Result where D: Display + Send + Sync + 'static, F: FnOnce() -> D; } /// Equivalent to Ok::<_, eyre::Error>(value). /// /// This simplifies creation of an eyre::Result in places where type inference /// cannot deduce the `E` type of the result — without needing to write /// `Ok::<_, eyre::Error>(value)`. /// /// One might think that `eyre::Result::Ok(value)` would work in such cases /// but it does not. /// /// ```console /// error[E0282]: type annotations needed for `std::result::Result` /// --> src/main.rs:11:13 /// | /// 11 | let _ = eyre::Result::Ok(1); /// | - ^^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result` /// | | /// | consider giving this pattern the explicit type `std::result::Result`, where the type parameter `E` is specified /// ``` #[allow(non_snake_case)] pub fn Ok(t: T) -> Result { Result::Ok(t) } // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { use crate::Report; use alloc::fmt; use core::fmt::{Arguments, Debug, Display}; pub use alloc::format; pub use core::format_args; pub use core::result::Result::Err; #[doc(hidden)] pub mod kind { pub use crate::kind::{AdhocKind, TraitKind}; pub use crate::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) } #[doc(hidden)] #[cold] #[cfg_attr(track_caller, track_caller)] pub fn format_err(args: Arguments<'_>) -> Report { #[cfg(eyre_no_fmt_arguments_as_str)] let fmt_arguments_as_str: Option<&str> = None; #[cfg(not(eyre_no_fmt_arguments_as_str))] let fmt_arguments_as_str = args.as_str(); if let Some(message) = fmt_arguments_as_str { // eyre!("literal"), can downcast to &'static str Report::msg(message) } else { // eyre!("interpolate {var}"), can downcast to String Report::msg(fmt::format(args)) } } } eyre-0.6.12/src/macros.rs000064400000000000000000000101021046102023000132710ustar 00000000000000/// Return early with an error. /// /// This macro is equivalent to `return Err(eyre!())`. /// /// # Example /// /// ``` /// # use eyre::{bail, Result}; /// # /// # fn has_permission(user: usize, resource: usize) -> bool { /// # true /// # } /// # /// # fn main() -> Result<()> { /// # let user = 0; /// # let resource = 0; /// # /// if !has_permission(user, resource) { /// bail!("permission denied for accessing {}", resource); /// } /// # Ok(()) /// # } /// ``` /// /// ``` /// # use eyre::{bail, Result}; /// # use thiserror::Error; /// # /// # const MAX_DEPTH: usize = 1; /// # /// #[derive(Error, Debug)] /// enum ScienceError { /// #[error("recursion limit exceeded")] /// RecursionLimitExceeded, /// # #[error("...")] /// # More = (stringify! { /// ... /// # }, 1).1, /// } /// /// # fn main() -> Result<()> { /// # let depth = 0; /// # let err: &'static dyn std::error::Error = &ScienceError::RecursionLimitExceeded; /// # /// if depth > MAX_DEPTH { /// bail!(ScienceError::RecursionLimitExceeded); /// } /// # Ok(()) /// # } /// ``` #[macro_export] macro_rules! bail { ($msg:literal $(,)?) => { return $crate::private::Err($crate::eyre!($msg)); }; ($err:expr $(,)?) => { return $crate::private::Err($crate::eyre!($err)); }; ($fmt:expr, $($arg:tt)*) => { return $crate::private::Err($crate::eyre!($fmt, $($arg)*)); }; } /// Return early with an error if a condition is not satisfied. /// /// This macro is equivalent to `if !$cond { return Err(eyre!()); }`. /// /// Analogously to `assert!`, `ensure!` takes a condition and exits the function /// if the condition fails. Unlike `assert!`, `ensure!` returns an `eyre::Result` /// rather than panicking. /// /// # Example /// /// ``` /// # use eyre::{ensure, Result}; /// # /// # fn main() -> Result<()> { /// # let user = 0; /// # /// ensure!(user == 0, "only user 0 is allowed"); /// # Ok(()) /// # } /// ``` /// /// ``` /// # use eyre::{ensure, Result}; /// # use thiserror::Error; /// # /// # const MAX_DEPTH: usize = 1; /// # /// #[derive(Error, Debug)] /// enum ScienceError { /// #[error("recursion limit exceeded")] /// RecursionLimitExceeded, /// # #[error("...")] /// # More = (stringify! { /// ... /// # }, 1).1, /// } /// /// # fn main() -> Result<()> { /// # let depth = 0; /// # /// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded); /// # Ok(()) /// # } /// ``` #[macro_export] macro_rules! ensure { ($cond:expr $(,)?) => { if !$cond { $crate::ensure!($cond, concat!("Condition failed: `", stringify!($cond), "`")) } }; ($cond:expr, $msg:literal $(,)?) => { if !$cond { return $crate::private::Err($crate::eyre!($msg)); } }; ($cond:expr, $err:expr $(,)?) => { if !$cond { return $crate::private::Err($crate::eyre!($err)); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { if !$cond { return $crate::private::Err($crate::eyre!($fmt, $($arg)*)); } }; } /// Construct an ad-hoc error from a string. /// /// This evaluates to a `Report`. It can take either just a string, or a format /// string with arguments. It also can take any custom type which implements /// `Debug` and `Display`. /// /// # Example /// /// ``` /// # type V = (); /// # /// use eyre::{eyre, Result}; /// /// fn lookup(key: &str) -> Result { /// if key.len() != 16 { /// return Err(eyre!("key length must be 16 characters, got {:?}", key)); /// } /// /// // ... /// # Ok(()) /// } /// ``` #[macro_export] macro_rules! eyre { ($msg:literal $(,)?) => ({ let error = $crate::private::format_err($crate::private::format_args!($msg)); error }); ($err:expr $(,)?) => ({ use $crate::private::kind::*; let error = match $err { error => (&error).eyre_kind().new(error), }; error }); ($fmt:expr, $($arg:tt)*) => { $crate::private::new_adhoc($crate::private::format!($fmt, $($arg)*)) }; } eyre-0.6.12/src/option.rs000064400000000000000000000005551046102023000133300ustar 00000000000000use crate::OptionExt; use core::fmt::{Debug, Display}; impl OptionExt for Option { #[track_caller] fn ok_or_eyre(self, message: M) -> crate::Result where M: Debug + Display + Send + Sync + 'static, { match self { Some(ok) => Ok(ok), None => Err(crate::Report::msg(message)), } } } eyre-0.6.12/src/ptr.rs000064400000000000000000000077031046102023000126270ustar 00000000000000use std::{marker::PhantomData, ptr::NonNull}; /// An owned pointer /// /// **NOTE**: Does not deallocate when dropped pub(crate) struct OwnedPtr { ptr: NonNull, } impl Copy for OwnedPtr {} impl Clone for OwnedPtr { fn clone(&self) -> Self { *self } } unsafe impl Send for OwnedPtr where T: Send {} unsafe impl Sync for OwnedPtr where T: Send {} impl OwnedPtr { pub(crate) fn new(value: T) -> Self { Self::from_boxed(Box::new(value)) } pub(crate) fn from_boxed(boxed: Box) -> Self { // Safety: `Box::into_raw` is guaranteed to be non-null Self { ptr: unsafe { NonNull::new_unchecked(Box::into_raw(boxed)) }, } } /// Convert the pointer to another type pub(crate) fn cast(self) -> OwnedPtr { OwnedPtr { ptr: self.ptr.cast(), } } /// Context the pointer into a Box /// /// # Safety /// /// Dropping the Box will deallocate a layout of `T` and run the destructor of `T`. /// /// A cast pointer must therefore be cast back to the original type before calling this method. pub(crate) unsafe fn into_box(self) -> Box { unsafe { Box::from_raw(self.ptr.as_ptr()) } } pub(crate) const fn as_ref(&self) -> RefPtr<'_, T> { RefPtr { ptr: self.ptr, _marker: PhantomData, } } pub(crate) fn as_mut(&mut self) -> MutPtr<'_, T> { MutPtr { ptr: self.ptr, _marker: PhantomData, } } } /// Convenience lifetime annotated mutable pointer which facilitates returning an inferred lifetime /// in a `fn` pointer. pub(crate) struct RefPtr<'a, T: ?Sized> { pub(crate) ptr: NonNull, _marker: PhantomData<&'a T>, } /// Safety: RefPtr indicates a shared reference to a value and as such exhibits the same Send + /// Sync behavior of &'a T unsafe impl<'a, T: ?Sized> Send for RefPtr<'a, T> where &'a T: Send {} unsafe impl<'a, T: ?Sized> Sync for RefPtr<'a, T> where &'a T: Sync {} impl<'a, T: ?Sized> Copy for RefPtr<'a, T> {} impl<'a, T: ?Sized> Clone for RefPtr<'a, T> { fn clone(&self) -> Self { *self } } impl<'a, T: ?Sized> RefPtr<'a, T> { pub(crate) fn new(ptr: &'a T) -> Self { Self { ptr: NonNull::from(ptr), _marker: PhantomData, } } /// Convert the pointer to another type pub(crate) fn cast(self) -> RefPtr<'a, U> { RefPtr { ptr: self.ptr.cast(), _marker: PhantomData, } } /// Returns a shared reference to the owned value /// /// # Safety /// /// See: [`NonNull::as_ref`] #[inline] pub(crate) unsafe fn as_ref(&self) -> &'a T { unsafe { self.ptr.as_ref() } } } /// Convenience lifetime annotated mutable pointer which facilitates returning an inferred lifetime /// in a `fn` pointer. pub(crate) struct MutPtr<'a, T: ?Sized> { pub(crate) ptr: NonNull, _marker: PhantomData<&'a mut T>, } /// Safety: RefPtr indicates an exclusive reference to a value and as such exhibits the same Send + /// Sync behavior of &'a mut T unsafe impl<'a, T: ?Sized> Send for MutPtr<'a, T> where &'a mut T: Send {} unsafe impl<'a, T: ?Sized> Sync for MutPtr<'a, T> where &'a mut T: Sync {} impl<'a, T: ?Sized> Copy for MutPtr<'a, T> {} impl<'a, T: ?Sized> Clone for MutPtr<'a, T> { fn clone(&self) -> Self { *self } } impl<'a, T: ?Sized> MutPtr<'a, T> { /// Convert the pointer to another type pub(crate) fn cast(self) -> MutPtr<'a, U> { MutPtr { ptr: self.ptr.cast(), _marker: PhantomData, } } /// Returns a mutable reference to the owned value with the lifetime decoupled from self /// /// # Safety /// /// See: [`NonNull::as_mut`] #[inline] pub(crate) unsafe fn into_mut(mut self) -> &'a mut T { unsafe { self.ptr.as_mut() } } } eyre-0.6.12/src/wrapper.rs000064400000000000000000000040461046102023000134770ustar 00000000000000use crate::StdError; use core::fmt::{self, Debug, Display}; #[repr(transparent)] pub(crate) struct DisplayError(pub(crate) M); #[repr(transparent)] /// Wraps a Debug + Display type as an error. /// /// Its Debug and Display impls are the same as the wrapped type. pub(crate) struct MessageError(pub(crate) M); pub(crate) struct NoneError; impl Debug for DisplayError where M: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl Display for DisplayError where M: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl StdError for DisplayError where M: Display + 'static {} impl Debug for MessageError where M: Display + Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) } } impl Display for MessageError where M: Display + Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl StdError for MessageError where M: Display + Debug + 'static {} impl Debug for NoneError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt("Option was None", f) } } impl Display for NoneError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt("Option was None", f) } } impl StdError for NoneError {} #[repr(transparent)] pub(crate) struct BoxedError(pub(crate) Box); impl Debug for BoxedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) } } impl Display for BoxedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } } impl StdError for BoxedError { #[cfg(backtrace)] fn backtrace(&self) -> Option<&crate::backtrace::Backtrace> { self.0.backtrace() } fn source(&self) -> Option<&(dyn StdError + 'static)> { self.0.source() } } eyre-0.6.12/tests/common/mod.rs000064400000000000000000000014001046102023000144300ustar 00000000000000#![allow(dead_code)] use eyre::{bail, set_hook, DefaultHandler, InstallError, Result}; use once_cell::sync::OnceCell; use std::io; pub fn bail_literal() -> Result<()> { bail!("oh no!"); } pub fn bail_fmt() -> Result<()> { bail!("{} {}!", "oh", "no"); } pub fn bail_error() -> Result<()> { bail!(io::Error::new(io::ErrorKind::Other, "oh no!")); } // Tests are multithreaded- use OnceCell to install hook once if auto-install // feature is disabled. pub fn maybe_install_handler() -> Result<(), InstallError> { static INSTALLER: OnceCell> = OnceCell::new(); if cfg!(not(feature = "auto-install")) { *INSTALLER.get_or_init(|| set_hook(Box::new(DefaultHandler::default_with))) } else { Ok(()) } } eyre-0.6.12/tests/compiletest.rs000064400000000000000000000002461046102023000147200ustar 00000000000000#[rustversion::attr(not(nightly), ignore)] #[cfg_attr(miri, ignore)] #[test] fn ui() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); } eyre-0.6.12/tests/drop/mod.rs000064400000000000000000000021661046102023000141160ustar 00000000000000use std::error::Error as StdError; use std::fmt::{self, Display}; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering::SeqCst; use std::sync::Arc; #[derive(Debug)] pub struct Flag { atomic: Arc, } impl Flag { pub fn new() -> Self { Flag { atomic: Arc::new(AtomicBool::new(false)), } } pub fn get(&self) -> bool { self.atomic.load(SeqCst) } } #[derive(Debug)] pub struct DetectDrop { has_dropped: Flag, label: &'static str, } impl DetectDrop { pub fn new(label: &'static str, has_dropped: &Flag) -> Self { DetectDrop { label, has_dropped: Flag { atomic: Arc::clone(&has_dropped.atomic), }, } } } impl StdError for DetectDrop {} impl Display for DetectDrop { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "oh no!") } } impl Drop for DetectDrop { fn drop(&mut self) { eprintln!("Dropping {}", self.label); let already_dropped = self.has_dropped.atomic.swap(true, SeqCst); assert!(!already_dropped); } } eyre-0.6.12/tests/test_autotrait.rs000064400000000000000000000003061046102023000154400ustar 00000000000000use eyre::Report; #[test] fn test_send() { fn assert_send() {} assert_send::(); } #[test] fn test_sync() { fn assert_sync() {} assert_sync::(); } eyre-0.6.12/tests/test_boxed.rs000064400000000000000000000031451046102023000145310ustar 00000000000000mod common; use self::common::maybe_install_handler; use eyre::{eyre, Report}; use std::error::Error as StdError; use std::io; use thiserror::Error; #[derive(Error, Debug)] #[error("outer")] struct MyError { source: io::Error, } #[test] fn test_boxed_str() { maybe_install_handler().unwrap(); let error = Box::::from("oh no!"); let error: Report = eyre!(error); assert_eq!("oh no!", error.to_string()); assert_eq!( "oh no!", error .downcast_ref::>() .unwrap() .to_string() ); } #[test] fn test_boxed_thiserror() { maybe_install_handler().unwrap(); let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!"), }; let error: Report = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } #[test] fn test_boxed_eyre() { maybe_install_handler().unwrap(); let error: Report = eyre!("oh no!").wrap_err("it failed"); let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } #[test] fn test_boxed_sources() { maybe_install_handler().unwrap(); let error = MyError { source: io::Error::new(io::ErrorKind::Other, "oh no!"), }; let error = Box::::from(error); let error: Report = eyre!(error).wrap_err("it failed"); assert_eq!("it failed", error.to_string()); assert_eq!("outer", error.source().unwrap().to_string()); assert_eq!( "oh no!", error.source().unwrap().source().unwrap().to_string() ); } eyre-0.6.12/tests/test_chain.rs000064400000000000000000000030251046102023000145070ustar 00000000000000mod common; use self::common::maybe_install_handler; use eyre::{eyre, Report}; fn error() -> Report { eyre!({ 0 }).wrap_err(1).wrap_err(2).wrap_err(3) } #[test] fn test_iter() { maybe_install_handler().unwrap(); let e = error(); let mut chain = e.chain(); assert_eq!("3", chain.next().unwrap().to_string()); assert_eq!("2", chain.next().unwrap().to_string()); assert_eq!("1", chain.next().unwrap().to_string()); assert_eq!("0", chain.next().unwrap().to_string()); assert!(chain.next().is_none()); assert!(chain.next_back().is_none()); } #[test] fn test_rev() { maybe_install_handler().unwrap(); let e = error(); let mut chain = e.chain().rev(); assert_eq!("0", chain.next().unwrap().to_string()); assert_eq!("1", chain.next().unwrap().to_string()); assert_eq!("2", chain.next().unwrap().to_string()); assert_eq!("3", chain.next().unwrap().to_string()); assert!(chain.next().is_none()); assert!(chain.next_back().is_none()); } #[test] fn test_len() { maybe_install_handler().unwrap(); let e = error(); let mut chain = e.chain(); assert_eq!(4, chain.len()); assert_eq!("3", chain.next().unwrap().to_string()); assert_eq!(3, chain.len()); assert_eq!("0", chain.next_back().unwrap().to_string()); assert_eq!(2, chain.len()); assert_eq!("2", chain.next().unwrap().to_string()); assert_eq!(1, chain.len()); assert_eq!("1", chain.next_back().unwrap().to_string()); assert_eq!(0, chain.len()); assert!(chain.next().is_none()); } eyre-0.6.12/tests/test_context.rs000064400000000000000000000074661046102023000151260ustar 00000000000000mod common; mod drop; use crate::common::maybe_install_handler; use crate::drop::{DetectDrop, Flag}; use eyre::{Report, Result, WrapErr}; use std::fmt::{self, Display}; use thiserror::Error; // https://github.com/dtolnay/eyre/issues/18 #[test] fn test_inference() -> Result<()> { let x = "1"; let y: u32 = x.parse().wrap_err("...")?; assert_eq!(y, 1); Ok(()) } macro_rules! context_type { ($name:ident) => { #[derive(Debug)] #[repr(C)] struct $name { _drop: DetectDrop, message: &'static str, } impl Display for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.message) } } }; } context_type!(HighLevel); context_type!(MidLevel); #[derive(Error, Debug)] #[error("{message}")] #[repr(C)] struct LowLevel { message: &'static str, drop: DetectDrop, } struct Dropped { low: Flag, mid: Flag, high: Flag, } impl Dropped { fn none(&self) -> bool { !self.low.get() && !self.mid.get() && !self.high.get() } fn all(&self) -> bool { self.low.get() && self.mid.get() && self.high.get() } } fn make_chain() -> (Report, Dropped) { let dropped = Dropped { low: Flag::new(), mid: Flag::new(), high: Flag::new(), }; let low = LowLevel { message: "no such file or directory", drop: DetectDrop::new("LowLevel", &dropped.low), }; // impl Report for Result let mid = Err::<(), LowLevel>(low) .wrap_err(MidLevel { message: "failed to load config", _drop: DetectDrop::new("MidLevel", &dropped.mid), }) .unwrap_err(); // impl Report for Result let high = Err::<(), Report>(mid) .wrap_err(HighLevel { message: "failed to start server", _drop: DetectDrop::new("HighLevel", &dropped.high), }) .unwrap_err(); (high, dropped) } #[test] fn test_downcast_ref() { maybe_install_handler().unwrap(); let (err, dropped) = make_chain(); assert!(!err.is::()); assert!(err.downcast_ref::().is_none()); assert!(err.is::()); let high = err.downcast_ref::().unwrap(); assert_eq!(high.to_string(), "failed to start server"); assert!(err.is::()); let mid = err.downcast_ref::().unwrap(); assert_eq!(mid.to_string(), "failed to load config"); assert!(err.is::()); let low = err.downcast_ref::().unwrap(); assert_eq!(low.to_string(), "no such file or directory"); assert!(dropped.none()); drop(err); assert!(dropped.all()); } #[test] fn test_downcast_high() { maybe_install_handler().unwrap(); let (err, dropped) = make_chain(); let err = err.downcast::().unwrap(); assert!(!dropped.high.get()); assert!(dropped.low.get() && dropped.mid.get()); drop(err); assert!(dropped.all()); } #[test] fn test_downcast_mid() { maybe_install_handler().unwrap(); let (err, dropped) = make_chain(); let err = err.downcast::().unwrap(); assert!(!dropped.mid.get()); assert!(dropped.low.get() && dropped.high.get()); drop(err); assert!(dropped.all()); } #[test] fn test_downcast_low() { maybe_install_handler().unwrap(); let (err, dropped) = make_chain(); let err = err.downcast::().unwrap(); assert!(!dropped.low.get()); assert!(dropped.mid.get() && dropped.high.get()); drop(err); assert!(dropped.all()); } #[test] fn test_unsuccessful_downcast() { maybe_install_handler().unwrap(); let (err, dropped) = make_chain(); let err = err.downcast::().unwrap_err(); assert!(dropped.none()); drop(err); assert!(dropped.all()); } eyre-0.6.12/tests/test_context_access.rs000064400000000000000000000003421046102023000164310ustar 00000000000000mod common; use crate::common::maybe_install_handler; #[test] fn test_context() { use eyre::{eyre, Report}; maybe_install_handler().unwrap(); let error: Report = eyre!("oh no!"); let _ = error.context(); } eyre-0.6.12/tests/test_convert.rs000064400000000000000000000012161046102023000151050ustar 00000000000000mod common; mod drop; use self::common::maybe_install_handler; use self::drop::{DetectDrop, Flag}; use eyre::{Report, Result}; use std::error::Error as StdError; #[test] fn test_convert() { maybe_install_handler().unwrap(); let has_dropped = Flag::new(); let error: Report = Report::new(DetectDrop::new("TestConvert", &has_dropped)); let box_dyn = Box::::from(error); assert_eq!("oh no!", box_dyn.to_string()); drop(box_dyn); assert!(has_dropped.get()); } #[test] fn test_question_mark() -> Result<(), Box> { fn f() -> Result<()> { Ok(()) } f()?; Ok(()) } eyre-0.6.12/tests/test_downcast.rs000064400000000000000000000064001046102023000152470ustar 00000000000000mod common; mod drop; use self::common::*; use self::drop::{DetectDrop, Flag}; use eyre::Report; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; #[test] fn test_downcast() { maybe_install_handler().unwrap(); #[cfg(not(eyre_no_fmt_arguments_as_str))] assert_eq!( "oh no!", bail_literal().unwrap_err().downcast::<&str>().unwrap(), ); #[cfg(eyre_no_fmt_arguments_as_str)] assert_eq!( "oh no!", bail_literal().unwrap_err().downcast::().unwrap(), ); assert_eq!( "oh no!", bail_fmt().unwrap_err().downcast::().unwrap(), ); assert_eq!( "oh no!", bail_error() .unwrap_err() .downcast::() .unwrap() .to_string(), ); } #[test] fn test_downcast_ref() { maybe_install_handler().unwrap(); #[cfg(not(eyre_no_fmt_arguments_as_str))] assert_eq!( "oh no!", *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(), ); #[cfg(eyre_no_fmt_arguments_as_str)] assert_eq!( "oh no!", *bail_literal() .unwrap_err() .downcast_ref::() .unwrap(), ); assert_eq!( "oh no!", bail_fmt().unwrap_err().downcast_ref::().unwrap(), ); assert_eq!( "oh no!", bail_error() .unwrap_err() .downcast_ref::() .unwrap() .to_string(), ); } #[test] fn test_downcast_mut() { maybe_install_handler().unwrap(); #[cfg(not(eyre_no_fmt_arguments_as_str))] assert_eq!( "oh no!", *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(), ); #[cfg(eyre_no_fmt_arguments_as_str)] assert_eq!( "oh no!", *bail_literal() .unwrap_err() .downcast_mut::() .unwrap(), ); assert_eq!( "oh no!", bail_fmt().unwrap_err().downcast_mut::().unwrap(), ); assert_eq!( "oh no!", bail_error() .unwrap_err() .downcast_mut::() .unwrap() .to_string(), ); } #[test] fn test_drop() { maybe_install_handler().unwrap(); let has_dropped = Flag::new(); let error: Report = Report::new(DetectDrop::new("DetectDrop", &has_dropped)); drop(error.downcast::().unwrap()); assert!(has_dropped.get()); } #[test] fn test_large_alignment() { maybe_install_handler().unwrap(); #[repr(align(64))] #[derive(Debug)] struct LargeAlignedError(&'static str); impl Display for LargeAlignedError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.0) } } impl StdError for LargeAlignedError {} let error = Report::new(LargeAlignedError("oh no!")); assert_eq!( "oh no!", error.downcast_ref::().unwrap().0 ); } #[test] fn test_unsuccessful_downcast() { maybe_install_handler().unwrap(); let mut error = bail_error().unwrap_err(); assert!(error.downcast_ref::<&str>().is_none()); assert!(error.downcast_mut::<&str>().is_none()); assert!(error.downcast::<&str>().is_err()); } eyre-0.6.12/tests/test_fmt.rs000064400000000000000000000042501046102023000142140ustar 00000000000000mod common; use self::common::maybe_install_handler; use eyre::{bail, Result, WrapErr}; use std::io; fn f() -> Result<()> { bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!")); } fn g() -> Result<()> { f().wrap_err("f failed") } fn h() -> Result<()> { g().wrap_err("g failed") } const EXPECTED_ALTDISPLAY_F: &str = "oh no!"; const EXPECTED_ALTDISPLAY_G: &str = "f failed: oh no!"; const EXPECTED_ALTDISPLAY_H: &str = "g failed: f failed: oh no!"; const EXPECTED_DEBUG_F: &str = "oh no!"; const EXPECTED_DEBUG_G: &str = "\ f failed Caused by: oh no!\ "; const EXPECTED_DEBUG_H: &str = "\ g failed Caused by: 0: f failed 1: oh no!\ "; const EXPECTED_ALTDEBUG_F: &str = "\ Custom { kind: PermissionDenied, error: \"oh no!\", }\ "; const EXPECTED_ALTDEBUG_G: &str = "\ Error { msg: \"f failed\", source: Custom { kind: PermissionDenied, error: \"oh no!\", }, }\ "; const EXPECTED_ALTDEBUG_H: &str = "\ Error { msg: \"g failed\", source: Error { msg: \"f failed\", source: Custom { kind: PermissionDenied, error: \"oh no!\", }, }, }\ "; #[test] fn test_display() { maybe_install_handler().unwrap(); assert_eq!("g failed", h().unwrap_err().to_string()); } #[test] fn test_altdisplay() { maybe_install_handler().unwrap(); assert_eq!(EXPECTED_ALTDISPLAY_F, format!("{:#}", f().unwrap_err())); assert_eq!(EXPECTED_ALTDISPLAY_G, format!("{:#}", g().unwrap_err())); assert_eq!(EXPECTED_ALTDISPLAY_H, format!("{:#}", h().unwrap_err())); } #[test] #[cfg_attr(any(backtrace, track_caller), ignore)] fn test_debug() { maybe_install_handler().unwrap(); assert_eq!(EXPECTED_DEBUG_F, format!("{:?}", f().unwrap_err())); assert_eq!(EXPECTED_DEBUG_G, format!("{:?}", g().unwrap_err())); assert_eq!(EXPECTED_DEBUG_H, format!("{:?}", h().unwrap_err())); } #[test] fn test_altdebug() { maybe_install_handler().unwrap(); assert_eq!(EXPECTED_ALTDEBUG_F, format!("{:#?}", f().unwrap_err())); assert_eq!(EXPECTED_ALTDEBUG_G, format!("{:#?}", g().unwrap_err())); assert_eq!(EXPECTED_ALTDEBUG_H, format!("{:#?}", h().unwrap_err())); } eyre-0.6.12/tests/test_location.rs000064400000000000000000000112041046102023000152330ustar 00000000000000use std::panic::Location; use eyre::{OptionExt as _, WrapErr}; struct LocationHandler { actual: Option<&'static str>, expected: &'static str, } impl LocationHandler { fn new(expected: &'static str) -> Self { LocationHandler { actual: None, expected, } } } impl eyre::EyreHandler for LocationHandler { fn debug( &self, _error: &(dyn std::error::Error + 'static), _f: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { // we assume that if the compiler is new enough to support // `track_caller` that we will always have `actual` be `Some`, so we can // safely skip the assertion if the location is `None` which should only // happen in older rust versions. if let Some(actual) = self.actual { assert_eq!(self.expected, actual); } Ok(()) } fn track_caller(&mut self, location: &'static Location<'static>) { dbg!(location); self.actual = Some(location.file()); } } #[test] fn test_wrap_err() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); let err = read_path("totally_fake_path") .wrap_err("oopsie") .unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[cfg(not(miri))] fn read_path(path: &str) -> Result { std::fs::read_to_string(path) } #[cfg(miri)] fn read_path(_path: &str) -> Result { // Miri doesn't support reading files, so we just return an error Err(std::io::Error::new( std::io::ErrorKind::Other, "Miri doesn't support reading files", )) } #[test] fn test_wrap_err_with() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); let err = read_path("totally_fake_path") .wrap_err_with(|| "oopsie") .unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_option_ok_or_eyre() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); let err = None::<()>.ok_or_eyre("oopsie").unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_context() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); let err = read_path("totally_fake_path") .context("oopsie") .unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_with_context() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); let err = read_path("totally_fake_path") .with_context(|| "oopsie") .unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_option_compat_wrap_err() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); use eyre::ContextCompat; let err = None::<()>.wrap_err("oopsie").unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_option_compat_wrap_err_with() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); use eyre::ContextCompat; let err = None::<()>.wrap_err_with(|| "oopsie").unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_option_compat_context() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); use eyre::ContextCompat; let err = None::<()>.context("oopsie").unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } #[test] fn test_option_compat_with_context() { let _ = eyre::set_hook(Box::new(|_e| { let expected_location = file!(); Box::new(LocationHandler::new(expected_location)) })); use eyre::ContextCompat; let err = None::<()>.with_context(|| "oopsie").unwrap_err(); // should panic if the location isn't in our crate println!("{:?}", err); } eyre-0.6.12/tests/test_macros.rs000064400000000000000000000046041046102023000147150ustar 00000000000000#![allow(clippy::eq_op)] mod common; use self::common::*; use eyre::{ensure, eyre, Result}; use std::cell::Cell; use std::future::Future; use std::pin::Pin; use std::task::Poll; #[test] fn test_messages() { maybe_install_handler().unwrap(); assert_eq!("oh no!", bail_literal().unwrap_err().to_string()); assert_eq!("oh no!", bail_fmt().unwrap_err().to_string()); assert_eq!("oh no!", bail_error().unwrap_err().to_string()); } #[test] fn test_ensure() { maybe_install_handler().unwrap(); let f = || -> Result<()> { ensure!(1 + 1 == 2, "This is correct"); Ok(()) }; assert!(f().is_ok()); let v = 1; let f = || -> Result<()> { ensure!(v + v == 2, "This is correct, v: {}", v); Ok(()) }; assert!(f().is_ok()); let f = || -> Result<()> { ensure!(v + v == 1, "This is not correct, v: {}", v); Ok(()) }; assert!(f().is_err()); let f = || { ensure!(v + v == 1); Ok(()) }; assert_eq!( f().unwrap_err().to_string(), "Condition failed: `v + v == 1`", ); } #[test] fn test_temporaries() { struct Ready(Option); impl Unpin for Ready {} impl Future for Ready { type Output = T; fn poll(mut self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> Poll { Poll::Ready(self.0.take().unwrap()) } } fn require_send_sync(_: impl Send + Sync) {} require_send_sync(async { // If eyre hasn't dropped any temporary format_args it creates by the // time it's done evaluating, those will stick around until the // semicolon, which is on the other side of the await point, making the // enclosing future non-Send. let _ = Ready(Some(eyre!("..."))).await; }); fn message(cell: Cell<&str>) -> &str { cell.get() } require_send_sync(async { let _ = Ready(Some(eyre!(message(Cell::new("..."))))).await; }); } #[test] #[cfg(not(eyre_no_fmt_args_capture))] fn test_capture_format_args() { maybe_install_handler().unwrap(); let var = 42; let err = eyre!("interpolate {var}"); assert_eq!("interpolate 42", err.to_string()); } #[test] fn test_brace_escape() { maybe_install_handler().unwrap(); let err = eyre!("unterminated ${{..}} expression"); assert_eq!("unterminated ${..} expression", err.to_string()); } eyre-0.6.12/tests/test_no_install.rs000064400000000000000000000011331046102023000155650ustar 00000000000000#![cfg(not(feature = "auto-install"))] use eyre::{eyre, set_hook, DefaultHandler, Report}; #[test] fn test_no_hook_panic() { let panic_res = std::panic::catch_unwind(|| eyre!("this will never be displayed")); assert!(panic_res.is_err()); let downcast_res = panic_res.unwrap_err().downcast::(); assert_eq!( *downcast_res.unwrap(), "a handler must always be installed if the `auto-install` feature is disabled" ); assert!(set_hook(Box::new(DefaultHandler::default_with)).is_ok()); let _error: Report = eyre!("this will be displayed if returned"); } eyre-0.6.12/tests/test_option.rs000064400000000000000000000004751046102023000147430ustar 00000000000000mod common; use self::common::maybe_install_handler; use eyre::OptionExt; #[test] fn test_option_ok_or_eyre() { maybe_install_handler().unwrap(); let option: Option<()> = None; let result = option.ok_or_eyre("static str error"); assert_eq!(result.unwrap_err().to_string(), "static str error"); } eyre-0.6.12/tests/test_pyo3.rs000064400000000000000000000013431046102023000143200ustar 00000000000000#![cfg(feature = "pyo3")] use pyo3::prelude::*; use eyre::{bail, Result, WrapErr}; fn f() -> Result<()> { use std::io; bail!(io::Error::new(io::ErrorKind::PermissionDenied, "oh no!")); } fn g() -> Result<()> { f().wrap_err("f failed") } fn h() -> Result<()> { g().wrap_err("g failed") } #[test] fn test_pyo3_exception_contents() { use pyo3::types::IntoPyDict; let err = h().unwrap_err(); let expected_contents = format!("{:?}", err); let pyerr = PyErr::from(err); Python::with_gil(|py| { let locals = [("err", pyerr)].into_py_dict(py); let pyerr = py.run("raise err", None, Some(locals)).unwrap_err(); assert_eq!(pyerr.value(py).to_string(), expected_contents); }) } eyre-0.6.12/tests/test_repr.rs000064400000000000000000000013161046102023000143760ustar 00000000000000mod common; mod drop; use self::common::maybe_install_handler; use self::drop::{DetectDrop, Flag}; use eyre::Report; use std::marker::Unpin; use std::mem; #[test] fn test_error_size() { assert_eq!(mem::size_of::(), mem::size_of::()); } #[test] fn test_null_pointer_optimization() { assert_eq!( mem::size_of::>(), mem::size_of::() ); } #[test] fn test_autotraits() { fn assert() {} assert::(); } #[test] fn test_drop() { maybe_install_handler().unwrap(); let has_dropped = Flag::new(); drop(Report::new(DetectDrop::new("TestDrop", &has_dropped))); assert!(has_dropped.get()); } eyre-0.6.12/tests/test_source.rs000064400000000000000000000031721046102023000147300ustar 00000000000000mod common; use self::common::maybe_install_handler; use eyre::{eyre, Report}; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; #[derive(Debug)] enum TestError { Io(io::Error), } impl Display for TestError { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { TestError::Io(e) => Display::fmt(e, formatter), } } } impl StdError for TestError { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { TestError::Io(io) => Some(io), } } } #[test] fn test_literal_source() { maybe_install_handler().unwrap(); let error: Report = eyre!("oh no!"); assert!(error.source().is_none()); } #[test] fn test_variable_source() { maybe_install_handler().unwrap(); let msg = "oh no!"; let error = eyre!(msg); assert!(error.source().is_none()); let msg = msg.to_owned(); let error: Report = eyre!(msg); assert!(error.source().is_none()); } #[test] fn test_fmt_source() { maybe_install_handler().unwrap(); let error: Report = eyre!("{} {}!", "oh", "no"); assert!(error.source().is_none()); } #[test] fn test_io_source() { maybe_install_handler().unwrap(); let io = io::Error::new(io::ErrorKind::Other, "oh no!"); let error: Report = eyre!(TestError::Io(io)); assert_eq!("oh no!", error.source().unwrap().to_string()); } #[test] fn test_eyre_from_eyre() { maybe_install_handler().unwrap(); let error: Report = eyre!("oh no!").wrap_err("context"); let error = eyre!(error); assert_eq!("oh no!", error.source().unwrap().to_string()); } eyre-0.6.12/tests/test_toolchain.rs000064400000000000000000000016421046102023000154100ustar 00000000000000// These tests check our build script against rustversion. #[rustversion::attr(not(nightly), ignore)] #[test] fn nightlytest() { if !cfg!(nightly) { panic!("nightly feature isn't set when the toolchain is nightly."); } if cfg!(any(beta, stable)) { panic!("beta, stable, and nightly are mutually exclusive features.") } } #[rustversion::attr(not(beta), ignore)] #[test] fn betatest() { if !cfg!(beta) { panic!("beta feature is not set when the toolchain is beta."); } if cfg!(any(nightly, stable)) { panic!("beta, stable, and nightly are mutually exclusive features.") } } #[rustversion::attr(not(stable), ignore)] #[test] fn stabletest() { if !cfg!(stable) { panic!("stable feature is not set when the toolchain is stable."); } if cfg!(any(nightly, beta)) { panic!("beta, stable, and nightly are mutually exclusive features.") } }