Merge remote-tracking branch 'origin/master' into color-eyre

This commit is contained in:
Freja Roberts 2023-12-08 16:56:40 +01:00
commit 57216d0b14
20 changed files with 583 additions and 180 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/target
**/*.rs.bk
Cargo.lock
tags

181
CONTRIBUTING.md Normal file
View File

@ -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:

View File

@ -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

View File

@ -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
<!-- next-url -->
[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

View File

@ -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="<!-- next-header -->"
replace="<!-- next-header -->\n\n## [Unreleased] - ReleaseDate"
exactly=1
[[package.metadata.release.pre-release-replacements]]
file="CHANGELOG.md"
search="<!-- next-url -->"
replace="<!-- next-url -->\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

View File

@ -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;

53
color-spantrace/build.rs Normal file
View File

@ -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<VersionInfo> {
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)
}

View File

@ -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:
//!
//! <pre><font color="#4E9A06"><b></b></font> cargo run --example usage
//! <pre><font color="#4E9A06"><b></b></font> cargo run --example color-spantrace-usage
//! <font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.04s
//! <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/usage`
//! <font color="#4E9A06"><b> Running</b></font> `target/debug/examples/color-spantrace-usage`
//! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//!
//! 0: <font color="#F15D22">usage::two</font>
//! at <font color="#75507B">examples/usage.rs</font>:<font color="#75507B">18</font>
//! 1: <font color="#F15D22">usage::one</font> with <font color="#34E2E2">i=42</font>
//! at <font color="#75507B">examples/usage.rs</font>:<font color="#75507B">13</font></pre>
//! 0: <font color="#F15D22">color-spantrace-usage::two</font>
//! at <font color="#75507B">examples/color-spantrace-usage.rs</font>:<font color="#75507B">18</font>
//! 1: <font color="#F15D22">color-spantrace-usage::one</font> with <font color="#34E2E2">i=42</font>
//! at <font color="#75507B">examples/color-spantrace-usage.rs</font>:<font color="#75507B">13</font></pre>
//!
//! [`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,

View File

@ -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
<!-- next-url -->
[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

View File

@ -1,6 +1,6 @@
[package]
name = "eyre"
version = "0.6.8"
version = "0.6.10"
authors = ["David Tolnay <dtolnay@gmail.com>", "Jane Lusby <jlusby42@gmail.com>"]
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 = "<!-- next-header -->"
replace = "<!-- next-header -->\n\n## [Unreleased] - ReleaseDate"
exactly = 1
[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
search = "<!-- next-url -->"
replace = "<!-- next-url -->\n[Unreleased]: https://github.com/eyre-rs/{{crate_name}}/compare/{{tag_name}}...HEAD"
exactly = 1

View File

@ -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<ExitStatus> {
.ok()
}
fn rustc_minor_version() -> Option<u32> {
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<VersionInfo> {
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)
}

View File

@ -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<T, E = Report> = core::result::Result<T, E>;
/// # 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<T, E>: 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<T, E>: 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<T>: context::private::Sealed {
/// Transform the [`Option<T>`] into a [`Result<T, E>`],
/// 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<M>(self, message: M) -> crate::Result<T>
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<T>: 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 &mdash; 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<i32, E>`
/// --> 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<i32, E>`, where the type parameter `E` is specified
/// ```
#[allow(non_snake_case)]
pub fn Ok<T>(t: T) -> Result<T> {
Result::Ok(t)
}
// Not public API. Referenced by macro-generated code.
#[doc(hidden)]
pub mod private {

View File

@ -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!(<args>))`.
///
/// # 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!(<other args>)); }`.
///
/// 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`.
///

14
eyre/src/option.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::OptionExt;
use core::fmt::{Debug, Display};
impl<T> OptionExt<T> for Option<T> {
fn ok_or_eyre<M>(self, message: M) -> crate::Result<T>
where
M: Debug + Display + Send + Sync + 'static,
{
match self {
Some(ok) => Ok(ok),
None => Err(crate::Report::msg(message)),
}
}
}

View File

@ -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<eyre::Error> for SomeWrappingErr {
fn from(err: eyre::Error) -> Self {
SomeWrappingErr { err }
}
}
#[test]

15
eyre/tests/test_option.rs Normal file
View File

@ -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");
}

View File

@ -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);
})
}

View File

@ -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.")
}
}