fix: remove ambiguity between options and results trait methods

1. Remove the conditional trait methods `context` and `with_context`
   from `WrapErr`
2. Remove ambiguity between "wrapping errors" with another, and
   converting a `None` value to an error.

The anyhow compatibility methods (`context` and `with_context`) were
introduced as conditional required methods for the trait `WrapErr`, as
well as another separate trait `ContextCompat`, implemented for options
*and* results.

The latter trait also provided methods `wrap_err` and `wrap_err_with`.
This led to confusion as the two distinct traits had the same methods,
one was implemented on results, and the other on both options and
results.

Furthermore, wrapping options does not align with our error model where
errors are wrapped around other errors; an error wrapping `None` should
just be a single error and should not be wrapped.

See: #149
This commit is contained in:
Freja Roberts 2024-01-24 17:32:27 +01:00
parent e570151950
commit a03951740c
3 changed files with 21 additions and 51 deletions

View File

@ -61,42 +61,34 @@ where
Err(e) => Err(e.ext_report(msg())),
}
}
}
#[cfg(feature = "anyhow")]
fn context<D>(self, msg: D) -> Result<T, Report>
#[cfg(feature = "anyhow")]
impl<T, E> crate::ContextCompat<T> for Result<T, E>
where
Self: WrapErr<T, E>,
{
#[track_caller]
fn context<D>(self, msg: D) -> crate::Result<T, Report>
where
D: Display + Send + Sync + 'static,
{
self.wrap_err(msg)
}
#[cfg(feature = "anyhow")]
fn with_context<D, F>(self, msg: F) -> Result<T, Report>
#[track_caller]
fn with_context<D, F>(self, f: F) -> crate::Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D,
{
self.wrap_err_with(msg)
self.wrap_err_with(f)
}
}
#[cfg(feature = "anyhow")]
impl<T> crate::ContextCompat<T> for Option<T> {
fn wrap_err<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
{
self.context(msg)
}
fn wrap_err_with<D, F>(self, msg: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D,
{
self.with_context(msg)
}
#[track_caller]
fn context<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
@ -107,6 +99,7 @@ impl<T> crate::ContextCompat<T> for Option<T> {
}
}
#[track_caller]
fn with_context<D, F>(self, msg: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,

View File

@ -1125,21 +1125,6 @@ pub trait WrapErr<T, E>: context::private::Sealed {
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
/// Compatibility re-export of wrap_err for interop with `anyhow`
#[cfg(feature = "anyhow")]
#[cfg_attr(track_caller, track_caller)]
fn context<D>(self, msg: D) -> Result<T, Report>
where
D: Display + Send + Sync + 'static;
/// Compatibility re-export of wrap_err_with for interop with `anyhow`
#[cfg(feature = "anyhow")]
#[cfg_attr(track_caller, track_caller)]
fn with_context<D, F>(self, f: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
}
/// Provides the [`ok_or_eyre`][OptionExt::ok_or_eyre] method for [`Option`].
@ -1197,7 +1182,8 @@ pub trait OptionExt<T>: context::private::Sealed {
M: Debug + Display + Send + Sync + 'static;
}
/// Provides the `context` method for `Option` when porting from `anyhow`
/// Provides the `context` and `with_context` methods for `Result` and `Option` to enhance
/// compatibility when porting from anyhow.
///
/// This trait is sealed and cannot be implemented for types outside of
/// `eyre`.
@ -1256,19 +1242,6 @@ pub trait ContextCompat<T>: context::private::Sealed {
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<D>(self, msg: D) -> Result<T, Report>
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<D, F>(self, f: F) -> Result<T, Report>
where
D: Display + Send + Sync + 'static,
F: FnOnce() -> D;
}
/// Equivalent to Ok::<_, eyre::Error>(value).

View File

@ -101,6 +101,8 @@ fn test_option_ok_or_eyre() {
#[cfg(feature = "anyhow")]
#[test]
fn test_context() {
use eyre::ContextCompat;
let _ = eyre::set_hook(Box::new(|_e| {
let expected_location = file!();
Box::new(LocationHandler::new(expected_location))
@ -117,6 +119,8 @@ fn test_context() {
#[cfg(feature = "anyhow")]
#[test]
fn test_with_context() {
use eyre::ContextCompat;
let _ = eyre::set_hook(Box::new(|_e| {
let expected_location = file!();
Box::new(LocationHandler::new(expected_location))
@ -139,7 +143,7 @@ fn test_option_compat_wrap_err() {
}));
use eyre::ContextCompat;
let err = None::<()>.wrap_err("oopsie").unwrap_err();
let err = None::<()>.context("oopsie").unwrap_err();
// should panic if the location isn't in our crate
println!("{:?}", err);
@ -154,7 +158,7 @@ fn test_option_compat_wrap_err_with() {
}));
use eyre::ContextCompat;
let err = None::<()>.wrap_err_with(|| "oopsie").unwrap_err();
let err = None::<()>.with_context(|| "oopsie").unwrap_err();
// should panic if the location isn't in our crate
println!("{:?}", err);