diff --git a/.gitignore b/.gitignore index 6936990..3d69c04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk Cargo.lock +tags diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6465433 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,181 @@ +# Welcome to the eyre contributing guide + +Thank you for investing your time in contributing to our project! Eyre is a +community owned and maintained project dedicated to improving the error +handling and error reporting experience of users of the Rust programming +language. + +Check out our community's[^1] [Code of +Conduct](https://www.rust-lang.org/policies/code-of-conduct) and feel free to +say hi on [Discord] if you'd like. It's a nice place to chat about eyre +development, ask questions, and get to know the other contributors and users in +a less formal setting. + +## The Eyre Organization + +The Eyre Organization is the group of people responsible for stewarding the +Eyre project. It handles things like merging pull requests, choosing project +direction, managing bugs / issues / feature requests, controlling access to +secrets, defining and enforcing best practices, etc. + +The eyre organization's governance is based on and inspired by +[sociocracy](https://www.sociocracyforall.org/sociocracy/), the Rust Project, +and the Bevy Organization. Many thanks to their great examples and resources. + +Note that you *do not* need to be a member of the Eyre Organization to +contribute to Eyre. Community contributors (this means you) can freely open +issues, submit pull requests, and review pull requests. + +### New contributor guide + +To get an overview of the project, read the [README](README.md). Here are some +resources to help you get started with open source contributions: + +- [Finding ways to contribute to open source on GitHub](https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github) +- [Set up Git](https://docs.github.com/en/get-started/quickstart/set-up-git) +- [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) +- [Collaborating with pull requests](https://docs.github.com/en/github/collaborating-with-pull-requests) + +Your first PR will be merged in no time! + +No matter how you're helping: thank you for contributing to Eyre! + +### Classifying PRs + +We use labels to organize our issues and PRs. + +Each [label](https://github.com/eyre-rs/eyre/labels) has a prefix denoting its category: + +* A: Area or subcrate (e.g. A-eyre, A-color-eyre, A-color-spantrace) +* C: Category (e.g. C-Breaking-Change, C-Code-Quality, C-Docs) +* P: Priority (e.g. P-Urgent, P-Important) +* S: Status (e.g. S-Blocked, S-Controversial, S-Needs-Design) +* Misc (e.g. "good first issue", "help wanted", "duplicate", "invalid", "wontfix") + +## Making changes to Eyre + +Most changes don't require much process. If your change is relatively straightforward, just do the following: + +1. A community member (that's you!) creates one of the following: + * [GitHub Discussions]: An informal discussion with the community. This is + the place to start if you want to propose a feature or specific + implementation and gathering community wisdom and advice before jumping + to solutions. + * [Issue](https://github.com/eyre-rs/eyre/issues): A formal way for us to + track a bug or feature. Please look for duplicates before opening a new + issue and consider starting with a Discussion. + * [Pull Request](https://github.com/eyre-rs/eyre/pulls) (or PR for short): + A request to merge code changes. This starts our "review process". You + are welcome to start with a pull request, but consider starting with an + Issue or Discussion for larger changes (or if you aren't certain about a + design). We don't want anyone to waste their time on code that didn't + have a chance to be merged! But conversely, sometimes PRs are the most + efficient way to propose a change. Just use your own judgement here. +2. Other community members review and comment in an ad-hoc fashion. Active + subject matter experts may be pulled into a thread using `@mentions`. If + your PR has been quiet for a while and is ready for review, feel free to + leave a message to "bump" the thread, or bring it up on [Discord] +3. Once they're content with the pull request (design, code quality, + documentation, tests), individual reviewers leave "Approved" reviews. +4. After consensus has been reached (typically two approvals from the community + or one for extremely simple changes) and CI passes, the + [S-Ready-For-Final-Review](https://github.com/eyre-rs/eyre/issues?q=is%3Aopen+is%3Aissue+label%3AS-Ready-For-Final-Review) + label is added. +5. When they find time, someone with merge rights performs a final code review + and queue the PR for merging. + +## How you can help + +If you've made it to this page, you're probably already convinced that Eyre is +a project you'd like to see thrive. But how can *you* help? + +No matter your experience level with Eyre or Rust or your level of commitment, +there are ways to meaningfully contribute. Take a look at the sections that +follow to pick a route (or five) that appeal to you. + +If you ever find yourself at a loss for what to do, or in need of mentorship or +advice on how to contribute to Eyre, feel free to ask in [Discord] and one of +our more experienced community members will be happy to help. + +### Writing Handlers + +You can improve Eyre's ecosystem by building your own +[EyreHandler](https://docs.rs/eyre/0.6.8/eyre/trait.EyreHandler.html) crates +like [color-eyre](https://github.com/eyre-rs/color-eyre/). The customizable +reporting of `eyre` is it's secret sauce, using that customizability in +creative ways and sharing your work is one of the best ways you can inspire +others and help grow our community. + +### Fixing bugs + +Bugs in Eyre are filed on the issue tracker using the [`C-bug`](https://github.com/eyre-rs/eyre/issues?q=is%3Aissue+is%3Aopen+label%3AC-Bug) label. + +If you're looking for an easy place to start, take a look at the [`good first +issue`](https://github.com/eyre-rs/eyre/labels/good%20first%20issue) label, and +feel free to ask questions on that issue's thread in question or on [Discord]. +You don't need anyone's permission to try fixing a bug or adding a simple +feature, but stating that you'd like to tackle an issue can be helpful to avoid +duplicated work. + +When you make a pull request that fixes an issue, include a line that says +`Fixes #X` (or "Closes"), where `X` is the issue number. This will cause the +issue in question to be closed when your PR is merged. + +General improvements to code quality are also welcome! +Eyre can always be safer, better tested, and more idiomatic. + +### Writing docs + +This is incredibly valuable, easily distributed work, but requires a bit of guidance: + +* Inaccurate documentation is worse than no documentation: prioritize fixing + broken docs. +* Code documentation (doc examples and in the examples folder) is easier to + maintain because the compiler will tell us when it breaks. +* Inline documentation should be technical and to the point. Link relevant + examples or other explanations if broader context is useful. + +### Reviewing others' work + +Reviewing others work with the aim of improving it is one of the most helpful +things you can do. You don't need to be an Elder Rustacean to be useful here: +anyone can catch missing tests, unclear docs, logic errors, and so on. If you +have specific skills (e.g. advanced familiarity with `unsafe` code, rendering +knowledge or web development experience) or personal experience with a problem, +try to prioritize those areas to ensure we can get appropriate expertise where +we need it. + +Focus on giving constructive, actionable feedback that results in real +improvements to code quality or end-user experience. If you don't understand +why an approach was taken, please ask! + +Provide actual code suggestions when that is helpful. Small changes work well +as comments or in-line suggestions on specific lines of codes. Larger changes +deserve a comment in the main thread, or a pull request to the original +author's branch (but please mention that you've made one). + +Once you're happy with the work and feel you're reasonably qualified to assess +quality in this particular area, leave your `Approved` review on the PR. If +you're new to GitHub, check out the [Pull Request Review +documentation](https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews). +Anyone can leave reviews ... no special permissions are required! + +There are three main places you can check for things to review: + +1. Pull request which are ready and in need of more reviews on + [eyre](https://github.com/eyre-rs/eyre/pulls?q=is%3Aopen+is%3Apr+-label%3AS-Ready-For-Final-Review+-draft%3A%3Atrue+-label%3AS-Needs-RFC+-reviewed-by%3A%40me+-author%3A%40me) +2. Pull requests on [eyre](https://github.com/eyre-rs/eyre/pulls) and the + [color-eyre](https://github.com/eyre-rs/color-eyre/pulls) repos. + +Not even our Circle Members are exempt from reviews! By giving feedback on this +work (and related supporting work), you can help us make sure our releases are +both high-quality and timely. + +Finally, if nothing brings you more satisfaction than seeing every last issue +labeled and all resolved issues closed, feel free to message any Eyre Circle +Member (currently @yaahc) for the triage role to help us keep things tidy. This +role only requires good faith and a basic understanding of our development +process. + +[Discord]: https://discord.gg/z94RqmUTKB +[^1]: Okay, I'll admit it, it's really just the Rust Project's CoC :sweat_smile: diff --git a/README.md b/README.md index 6215eea..7a27f01 100644 --- a/README.md +++ b/README.md @@ -174,23 +174,9 @@ avoid using `eyre::Report` as your public error type. ## No-std support -**NOTE**: tests are currently broken for `no_std` so I cannot guarantee that -everything works still. I'm waiting for upstream fixes to be merged rather than -fixing them myself, so bear with me. - -In no_std mode, the same API is almost all available and works the same way. To -depend on Eyre in no_std mode, disable our default enabled "std" feature in -Cargo.toml. A global allocator is required. - -```toml -[dependencies] -eyre = { version = "0.6", default-features = false } -``` - -Since the `?`-based error conversions would normally rely on the -`std::error::Error` trait which is only available through std, no_std mode will -require an explicit `.map_err(Report::msg)` when working with a non-Eyre error -type inside a function that returns Eyre's error type. +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 @@ -225,24 +211,30 @@ 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` intends for users to use the combinator functions provided by -`std` for converting `Option`s to `Result`s. So where you would write this with +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 = opt.context("new error message"); +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, Result}; +use eyre::{eyre, OptionExt, Result}; let opt: Option<()> = None; -let result: Result<()> = opt.ok_or_else(|| eyre!("new error message")); +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 diff --git a/color-spantrace/CHANGELOG.md b/color-spantrace/CHANGELOG.md index 3014914..1d3bcb0 100644 --- a/color-spantrace/CHANGELOG.md +++ b/color-spantrace/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +## [0.2.1] - 2023-11-17 +### Fixed +- Add license files [by erickt](https://github.com/eyre-rs/color-spantrace/pull/19) + ## [0.2.0] - 2022-01-12 ### Changed - Updated dependency versions to match latest tracing versions @@ -22,7 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support custom color themes for spantrace format -[Unreleased]: https://github.com/eyre-rs/color-spantrace/compare/v0.2.0...HEAD +[Unreleased]: https://github.com/eyre-rs/color-spantrace/compare/v0.2.1...HEAD +[0.2.1]: https://github.com/eyre-rs/color-spantrace/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/eyre-rs/color-spantrace/compare/v0.1.6...v0.2.0 [0.1.6]: https://github.com/eyre-rs/color-spantrace/compare/v0.1.5...v0.1.6 [v0.1.5]: https://github.com/eyre-rs/color-spantrace/releases/tag/v0.1.5 diff --git a/color-spantrace/Cargo.toml b/color-spantrace/Cargo.toml index 1707fc2..79e6b8a 100644 --- a/color-spantrace/Cargo.toml +++ b/color-spantrace/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "color-spantrace" -version = "0.2.0" +version = "0.2.1" description = "A pretty printer for tracing_error::SpanTrace based on color-backtrace" documentation = "https://docs.rs/color-spantrace" @@ -26,43 +26,5 @@ ansi-parser = "0.8" # used for testing color schemes all-features = true rustdoc-args = ["--cfg", "docsrs"] -[package.metadata.release] -dev-version = false - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "Unreleased" -replace="{{version}}" - -[[package.metadata.release.pre-release-replacements]] -file = "src/lib.rs" -search = "#!\\[doc\\(html_root_url.*" -replace = "#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]" -exactly = 1 - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "\\.\\.\\.HEAD" -replace="...{{tag_name}}" -exactly = 1 - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "ReleaseDate" -replace="{{date}}" - -[[package.metadata.release.pre-release-replacements]] -file="CHANGELOG.md" -search="" -replace="\n\n## [Unreleased] - ReleaseDate" -exactly=1 - -[[package.metadata.release.pre-release-replacements]] -file="CHANGELOG.md" -search="" -replace="\n[Unreleased]: https://github.com/eyre-rs/{{crate_name}}/compare/{{tag_name}}...HEAD" -exactly=1 - -[[example]] -name = "color-spantrace-usage" -path = "examples/usage.rs" +[package.metadata.workspaces] +independent = true diff --git a/color-spantrace/README.md b/color-spantrace/README.md index ce25bea..7205e61 100644 --- a/color-spantrace/README.md +++ b/color-spantrace/README.md @@ -58,7 +58,7 @@ println!("{}", color_spantrace::colorize(&span_trace)); ## Example -This example is taken from `examples/usage.rs`: +This example is taken from `examples/color-spantrace-usage.rs`: ```rust use tracing::instrument; diff --git a/color-spantrace/build.rs b/color-spantrace/build.rs new file mode 100644 index 0000000..aae2878 --- /dev/null +++ b/color-spantrace/build.rs @@ -0,0 +1,53 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; +use std::str; + +fn main() { + let version = match rustc_version_info() { + Some(version) => version, + None => return, + }; + version.toolchain.set_feature(); +} + +#[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 { + 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: u32 = 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 { toolchain }; + Some(version) +} diff --git a/color-spantrace/examples/usage.rs b/color-spantrace/examples/color-spantrace-usage.rs similarity index 100% rename from color-spantrace/examples/usage.rs rename to color-spantrace/examples/color-spantrace-usage.rs diff --git a/color-spantrace/src/lib.rs b/color-spantrace/src/lib.rs index 7d51313..214799b 100644 --- a/color-spantrace/src/lib.rs +++ b/color-spantrace/src/lib.rs @@ -45,25 +45,29 @@ //! //! ## Output Format //! -//! Running `examples/usage.rs` from the `color-spantrace` repo produces the following output: +//! Running `examples/color-spantrace-usage.rs` from the `color-spantrace` repo produces the following output: //! -//!
 cargo run --example usage
+//! 
 cargo run --example color-spantrace-usage
 //!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
-//!      Running `target/debug/examples/usage`
+//!      Running `target/debug/examples/color-spantrace-usage`
 //! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 //!
-//!  0: usage::two
-//!     at examples/usage.rs:18
-//!  1: usage::one with i=42
-//!     at examples/usage.rs:13
+//! 0: color-spantrace-usage::two +//! at examples/color-spantrace-usage.rs:18 +//! 1: color-spantrace-usage::one with i=42 +//! at examples/color-spantrace-usage.rs:13
//! //! [`tracing_error::SpanTrace`]: https://docs.rs/tracing-error/*/tracing_error/struct.SpanTrace.html //! [`color-backtrace`]: https://github.com/athre0z/color-backtrace -#![doc(html_root_url = "https://docs.rs/color-spantrace/0.2.0")] +#![doc(html_root_url = "https://docs.rs/color-spantrace/0.2.1")] +#![cfg_attr( + nightly, + feature(rustdoc_missing_doc_code_examples), + warn(rustdoc::missing_doc_code_examples) +)] #![warn( missing_debug_implementations, missing_docs, - rustdoc::missing_doc_code_examples, rust_2018_idioms, unreachable_pub, bad_style, @@ -74,7 +78,6 @@ overflowing_literals, path_statements, patterns_in_fns_without_body, - private_in_public, unconditional_recursion, unused, unused_allocation, diff --git a/eyre/CHANGELOG.md b/eyre/CHANGELOG.md index 2ca4285..9a75a23 100644 --- a/eyre/CHANGELOG.md +++ b/eyre/CHANGELOG.md @@ -8,47 +8,78 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +## [0.6.10] - 2023-12-07 +### 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) +- 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.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 -- Add `#[must_use]` to `Report` -- Add `must-install` feature to help reduce binary sizes when using a custom `EyreHandler` +- `#[must_use]` to `Report` +- `must-install` feature to help reduce binary sizes when using a custom `EyreHandler` ## [0.6.7] - 2022-02-24 ### Fixed -- added missing track_caller annotation to new format arg capture constructor +- missing track_caller annotation to new format arg capture constructor ## [0.6.6] - 2022-01-19 ### Added -- add support for format arguments capture on 1.58 and later +- support for format arguments capture on 1.58 and later ## [0.6.5] - 2021-01-05 ### Added -- add optional support for converting into `pyo3` exceptions +- optional support for converting into `pyo3` exceptions ## [0.6.4] - 2021-01-04 ### Fixed -- added missing track_caller annotations to `wrap_err` related trait methods +- missing track_caller annotations to `wrap_err` related trait methods ## [0.6.3] - 2020-11-10 ### Fixed -- added missing track_caller annotation to autoref specialization functions +- missing track_caller annotation to autoref specialization functions ## [0.6.2] - 2020-10-27 ### Fixed -- added missing track_caller annotation to new_adhoc function +- missing track_caller annotation to new_adhoc function ## [0.6.1] - 2020-09-28 ### Added -- support track_caller on rust versions where it is available +- support for track_caller on rust versions where it is available -[Unreleased]: https://github.com/eyre-rs/eyre/compare/v0.6.8...HEAD -[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 +[Unreleased]: https://github.com/eyre-rs/eyre/compare/v0.6.10...HEAD +[0.6.10]: https://github.com/eyre-rs/eyre/compare/v0.6.9...v0.6.10 +[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 diff --git a/eyre/Cargo.toml b/eyre/Cargo.toml index afa349d..6f69924 100644 --- a/eyre/Cargo.toml +++ b/eyre/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "eyre" -version = "0.6.8" +version = "0.6.10" authors = ["David Tolnay ", "Jane Lusby "] description = "Flexible concrete Error Reporting type built on std::error::Error with customizable Reports" documentation = "https://docs.rs/eyre" @@ -20,7 +20,7 @@ track-caller = [] [dependencies] indenter = { workspace = true } once_cell = { workspace = true } -pyo3 = { version = "0.13", optional = true, default-features = false } +pyo3 = { version = "0.20", optional = true, default-features = false } [dev-dependencies] futures = { version = "0.3", default-features = false } @@ -30,45 +30,12 @@ trybuild = { version = "1.0.19", features = ["diff"] } backtrace = "0.3.46" anyhow = "1.0.28" syn = { version = "2.0", features = ["full"] } -pyo3 = { version = "0.13", default-features = false, features = ["auto-initialize"] } +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.release] -dev-version = false +[package.metadata.workspaces] +independent = true -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "Unreleased" -replace = "{{version}}" - -[[package.metadata.release.pre-release-replacements]] -file = "src/lib.rs" -search = "#!\\[doc\\(html_root_url.*" -replace = "#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]" -exactly = 1 - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "\\.\\.\\.HEAD" -replace = "...{{tag_name}}" -exactly = 1 - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "ReleaseDate" -replace = "{{date}}" - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "" -replace = "\n\n## [Unreleased] - ReleaseDate" -exactly = 1 - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" -search = "" -replace = "\n[Unreleased]: https://github.com/eyre-rs/{{crate_name}}/compare/{{tag_name}}...HEAD" -exactly = 1 diff --git a/eyre/build.rs b/eyre/build.rs index 60371ab..e0b2d29 100644 --- a/eyre/build.rs +++ b/eyre/build.rs @@ -1,4 +1,5 @@ use std::env; +use std::ffi::OsString; use std::fs; use std::path::Path; use std::process::{Command, ExitStatus}; @@ -55,16 +56,18 @@ fn main() { _ => {} } - let rustc = match rustc_minor_version() { - Some(rustc) => rustc, + let version = match rustc_version_info() { + Some(version) => version, None => return, }; - if rustc < 52 { + version.toolchain.set_feature(); + + if version.minor < 52 { println!("cargo:rustc-cfg=eyre_no_fmt_arguments_as_str"); } - if rustc < 58 { + if version.minor < 58 { println!("cargo:rustc-cfg=eyre_no_fmt_args_capture"); } } @@ -86,13 +89,44 @@ fn compile_probe(probe: &str) -> Option { .ok() } -fn rustc_minor_version() -> Option { - let rustc = env::var_os("RUSTC")?; +// 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 1") { + let mut pieces = version.split(['.', ' ', '-']); + if pieces.next() != Some("rustc") { return None; } - pieces.next()?.parse().ok() + 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) } diff --git a/eyre/examples/usage.rs b/eyre/examples/eyre-usage.rs similarity index 100% rename from eyre/examples/usage.rs rename to eyre/examples/eyre-usage.rs diff --git a/eyre/src/lib.rs b/eyre/src/lib.rs index 35a9026..5c8d815 100644 --- a/eyre/src/lib.rs +++ b/eyre/src/lib.rs @@ -236,23 +236,9 @@ //! //! ## No-std support //! -//! **NOTE**: tests are currently broken for `no_std` so I cannot guarantee that -//! everything works still. I'm waiting for upstream fixes to be merged rather than -//! fixing them myself, so bear with me. -//! -//! In no_std mode, almost all the API is available and works the same way. To -//! depend on Eyre in no_std mode, disable our default enabled "std" feature in -//! Cargo.toml. A global allocator is required. -//! -//! ```toml -//! [dependencies] -//! eyre = { version = "0.6", default-features = false } -//! ``` -//! -//! Since the `?`-based error conversions would normally rely on the -//! `std::error::Error` trait which is only available through std, no_std mode will -//! require an explicit `.map_err(Report::msg)` when working with a non-Eyre error -//! type inside a function that returns Eyre's error type. +//! 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 //! @@ -287,27 +273,31 @@ //! `source`. With `Option` there is no source error to wrap, so `wrap_err` ends up //! being somewhat meaningless. //! -//! Instead `eyre` intends for users to use the combinator functions provided by -//! `std` for converting `Option`s to `Result`s. So where you would write this with +//! 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 = opt.context("new error message"); +//! 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, Result}; +//! 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: Result<()> = opt.ok_or_else(|| eyre!("new error message")); +//! 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 @@ -328,12 +318,15 @@ //! [`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.8")] +#![doc(html_root_url = "https://docs.rs/eyre/0.6.10")] +#![cfg_attr( + nightly, + feature(rustdoc_missing_doc_code_examples), + warn(rustdoc::missing_doc_code_examples) +)] #![warn( missing_debug_implementations, missing_docs, - // FIXME: this lint is currently nightly only - rustdoc::missing_doc_code_examples, unsafe_op_in_unsafe_fn, rust_2018_idioms, unreachable_pub, @@ -345,7 +338,6 @@ overflowing_literals, path_statements, patterns_in_fns_without_body, - private_in_public, unconditional_recursion, unused, unused_allocation, @@ -371,12 +363,13 @@ 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::Display; +use core::fmt::{Debug, Display}; use std::error::Error as StdError; @@ -721,7 +714,7 @@ pub trait EyreHandler: core::any::Any + Send + Sync { } } - Ok(()) + Result::Ok(()) } /// Store the location of the caller who constructed this error report @@ -843,7 +836,7 @@ impl EyreHandler for DefaultHandler { } } - Ok(()) + Result::Ok(()) } #[cfg(track_caller)] @@ -1096,6 +1089,13 @@ pub type Result = core::result::Result; /// # 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)] @@ -1125,6 +1125,61 @@ pub trait WrapErr: context::private::Sealed { 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 @@ -1198,6 +1253,29 @@ pub trait ContextCompat: context::private::Sealed { 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 { diff --git a/eyre/src/macros.rs b/eyre/src/macros.rs index 792a646..4820b28 100644 --- a/eyre/src/macros.rs +++ b/eyre/src/macros.rs @@ -1,6 +1,6 @@ /// Return early with an error. /// -/// This macro is equivalent to `return Err(From::from($err))`. +/// This macro is equivalent to `return Err(eyre!())`. /// /// # Example /// @@ -51,22 +51,22 @@ #[macro_export] macro_rules! bail { ($msg:literal $(,)?) => { - return $crate::private::Err($crate::eyre!($msg)); + return $crate::private::Err($crate::eyre!($msg).into()); }; ($err:expr $(,)?) => { - return $crate::private::Err($crate::eyre!($err)); + return $crate::private::Err($crate::eyre!($err).into()); }; ($fmt:expr, $($arg:tt)*) => { - return $crate::private::Err($crate::eyre!($fmt, $($arg)*)); + return $crate::private::Err($crate::eyre!($fmt, $($arg)*).into()); }; } /// Return early with an error if a condition is not satisfied. /// -/// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. +/// 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 `Error` +/// if the condition fails. Unlike `assert!`, `ensure!` returns an `eyre::Result` /// rather than panicking. /// /// # Example @@ -107,26 +107,31 @@ macro_rules! bail { /// ``` #[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)); + return $crate::private::Err($crate::eyre!($msg).into()); } }; ($cond:expr, $err:expr $(,)?) => { if !$cond { - return $crate::private::Err($crate::eyre!($err)); + return $crate::private::Err($crate::eyre!($err).into()); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { if !$cond { - return $crate::private::Err($crate::eyre!($fmt, $($arg)*)); + return $crate::private::Err($crate::eyre!($fmt, $($arg)*).into()); } }; } /// Construct an ad-hoc error from a string. /// -/// This evaluates to an `Error`. It can take either just a string, or a format +/// 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`. /// diff --git a/eyre/src/option.rs b/eyre/src/option.rs new file mode 100644 index 0000000..eaa1e84 --- /dev/null +++ b/eyre/src/option.rs @@ -0,0 +1,14 @@ +use crate::OptionExt; +use core::fmt::{Debug, Display}; + +impl OptionExt for Option { + 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)), + } + } +} diff --git a/eyre/tests/test_macros.rs b/eyre/tests/test_macros.rs index 2bcad38..9fddd8a 100644 --- a/eyre/tests/test_macros.rs +++ b/eyre/tests/test_macros.rs @@ -39,6 +39,34 @@ fn test_ensure() { Ok(()) }; assert!(f().is_err()); + + // Tests single-argument `ensure!` + let f = || -> Result<()> { + ensure!(v + v == 1); + Ok(()) + }; + assert_eq!( + f().unwrap_err().to_string(), + "Condition failed: `v + v == 1`", + ); + + // Tests automatically converting to external errors with ensure!() + let f = || -> Result<(), SomeWrappingErr> { + ensure!(false, "this will fail"); + Ok(()) + }; + assert!(f().is_err()); +} + +#[allow(dead_code)] +struct SomeWrappingErr { + err: eyre::Error, +} + +impl From for SomeWrappingErr { + fn from(err: eyre::Error) -> Self { + SomeWrappingErr { err } + } } #[test] diff --git a/eyre/tests/test_option.rs b/eyre/tests/test_option.rs new file mode 100644 index 0000000..7c9349a --- /dev/null +++ b/eyre/tests/test_option.rs @@ -0,0 +1,15 @@ +mod 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"); +} diff --git a/eyre/tests/test_pyo3.rs b/eyre/tests/test_pyo3.rs index 0ad71af..e7f58c1 100644 --- a/eyre/tests/test_pyo3.rs +++ b/eyre/tests/test_pyo3.rs @@ -28,6 +28,6 @@ fn test_pyo3_exception_contents() { 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.pvalue(py).to_string(), expected_contents); + assert_eq!(pyerr.value(py).to_string(), expected_contents); }) } diff --git a/eyre/tests/test_toolchain.rs b/eyre/tests/test_toolchain.rs new file mode 100644 index 0000000..0309398 --- /dev/null +++ b/eyre/tests/test_toolchain.rs @@ -0,0 +1,34 @@ +// 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.") + } +}