From b2179c1f73f6e0e6bcd92c82344cd85a9c9170d7 Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 17 Oct 2024 13:34:03 -0500 Subject: [PATCH] Show source by default, add feature to hide it close #59 Changes the default behavior so that the output in the readme now matches the output a user would see when they use the library. Specifically, it now includes the original `std::io::Error` in the output. It introduces a feature `anyhow`. By default: - By default: fs-err will include `std::io::Error` in the Display output, and return `None` from `Error::source()` - With `anyhow` fs-err will not include std::io::Error in the Display output, and return `Some(std::io::Error)` from `Error::source()` This is based on the guidance from #51. That discussion links to this 2020 discussion https://github.com/rust-lang/project-error-handling/issues/23 that suggests that you should either print the source and return `None` from `Error::source()` (https://doc.rust-lang.org/std/error/trait.Error.html#method.source) or not print anything and return `Some(E)`. This allows users of anyhow (or similar) libraries to configure fs-err for the behavior they desire, while not hiding information by default from unsuspecting adopters that are not using those libraries. It optimizes for the case of accidentally showing extra information that a user can then investigate and disable, rather than hiding information that the user might not realize is missing. --- .github/workflows/ci.yml | 6 ++++++ CHANGELOG.md | 2 ++ Cargo.toml | 3 +++ src/errors.rs | 26 ++++++++++++++++++++++++-- src/lib.rs | 4 ++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 798422a..64aae50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,3 +67,9 @@ jobs: command: check args: --features tokio if: ${{ matrix.rust_version == 'stable' || matrix.rust_version == 'beta' }} + + - name: cargo check --features anyhow + uses: actions-rs/cargo@v1 + with: + command: check + args: --features anyhow diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ad5ccc..7db14a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # fs-err Changelog +* Change errors to output original `std::io::Error` information Display by default. This functionality can be disabled for [anyhow](https://docs.rs/anyhow/latest/anyhow/) users by using the new feature `anyhow` ([#60](https://github.com/andrewhickman/fs-err/pull/60)). + ## 2.11.0 * Added the first line of the standard library documentation to each function's rustdocs, to make them more useful in IDEs ([#50](https://github.com/andrewhickman/fs-err/issues/45)) diff --git a/Cargo.toml b/Cargo.toml index 6c999e3..4d8cf44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,9 @@ serde_json = "1.0.64" [features] # Adds I/O safety traits, introduced in Rust 1.63 io_safety = [] +# Removes the original error text from Display and relies on a wrapper library +# (such as anyhow) to emit them via `Error::source()`. +anyhow = [] [package.metadata.release] tag-name = "{{version}}" diff --git a/src/errors.rs b/src/errors.rs index 22a0eaa..a8318d0 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -99,7 +99,12 @@ impl fmt::Display for Error { E::ReadAt => write!(formatter, "failed to read with offset from `{}`", path), #[cfg(unix)] E::WriteAt => write!(formatter, "failed to write with offset to `{}`", path), - } + }?; + + #[cfg(not(feature = "anyhow"))] + write!(formatter, " caused by: {}", self.source)?; + + Ok(()) } } @@ -108,6 +113,12 @@ impl StdError for Error { self.source() } + #[cfg(not(feature = "anyhow"))] + fn source(&self) -> Option<&(dyn StdError + 'static)> { + None + } + + #[cfg(feature = "anyhow")] fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.source) } @@ -188,7 +199,12 @@ impl fmt::Display for SourceDestError { SourceDestErrorKind::SymlinkDir => { write!(formatter, "failed to symlink dir from {} to {}", from, to) } - } + }?; + + #[cfg(not(feature = "anyhow"))] + write!(formatter, " caused by: {}", self.source)?; + + Ok(()) } } @@ -197,6 +213,12 @@ impl StdError for SourceDestError { self.source() } + #[cfg(not(feature = "anyhow"))] + fn source(&self) -> Option<&(dyn StdError + 'static)> { + None + } + + #[cfg(feature = "anyhow")] fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(&self.source) } diff --git a/src/lib.rs b/src/lib.rs index 19b8c8d..991dd52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,10 @@ failed to open file `does not exist.txt` caused by: The system cannot find the file specified. (os error 2) ``` +> Note: To bypass displaying the original error message you can enable the `anyhow` feature. +> When the `anyhow` feature is enabled `Error::source()` will return `Some` and the original +> error will not be `Display`-ed via fs-err. + # Usage fs-err's API is the same as [`std::fs`][std::fs], so migrating code to use it is easy.