pub trait ErrCtxExt<T>: Sized {
#[must_use = "an error wrap that isn't threaded via `?` swallows the underlying failure"]
fn err_ctx(self, context: &'static str) -> anyhow::Result<T>;
#[must_use = "an error wrap that isn't threaded via `?` swallows the underlying failure"]
fn err_ctx_with(self, context: String) -> anyhow::Result<T>;
}
impl<T, E> ErrCtxExt<T> for Result<T, E>
where
E: std::fmt::Display,
{
#[inline]
fn err_ctx(self, context: &'static str) -> anyhow::Result<T> {
self.map_err(|e| anyhow::anyhow!("{context}: {e}"))
}
#[inline]
fn err_ctx_with(self, context: String) -> anyhow::Result<T> {
self.map_err(|e| anyhow::anyhow!("{context}: {e}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct DisplayErr(&'static str);
impl std::fmt::Display for DisplayErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.0)
}
}
#[test]
fn err_ctx_static_str_context_matches_pre_lift_format_bytewise() {
let raw: Result<(), DisplayErr> = Err(DisplayErr("bad slot"));
let via_trait = raw.err_ctx("source").unwrap_err();
assert_eq!(format!("{via_trait}"), "source: bad slot");
}
#[test]
fn err_ctx_ok_arm_is_a_pure_passthrough() {
let raw: Result<i32, DisplayErr> = Ok(42);
assert_eq!(raw.err_ctx("noop").unwrap(), 42);
}
#[test]
fn err_ctx_with_owned_string_matches_pre_lift_format_bytewise() {
let raw: Result<(), DisplayErr> = Err(DisplayErr("bad slot"));
let dynamic_slug = format!("evaluate {:?}", "SomeVariant");
let via_trait = raw.err_ctx_with(dynamic_slug.clone()).unwrap_err();
assert_eq!(format!("{via_trait}"), format!("{dynamic_slug}: bad slot"));
}
#[test]
fn err_ctx_with_ok_arm_is_a_pure_passthrough() {
let raw: Result<&'static str, DisplayErr> = Ok("variant resolved");
assert_eq!(
raw.err_ctx_with("dynamic".to_string()).unwrap(),
"variant resolved"
);
}
#[test]
fn err_ctx_static_and_owned_peers_produce_identical_output_for_the_same_slug() {
let slug = "source";
let a: Result<(), DisplayErr> = Err(DisplayErr("bad slot"));
let b: Result<(), DisplayErr> = Err(DisplayErr("bad slot"));
assert_eq!(
format!("{}", a.err_ctx(slug).unwrap_err()),
format!("{}", b.err_ctx_with(slug.to_string()).unwrap_err()),
"static-str and owned-string peers must produce identical Display output"
);
}
#[test]
fn err_ctx_threads_the_underlying_display_verbatim() {
let underlying_display = format!("{}", DisplayErr("bad slot"));
let raw: Result<(), DisplayErr> = Err(DisplayErr("bad slot"));
let wrapped = raw.err_ctx("source").unwrap_err();
let wrapped_display = format!("{wrapped}");
assert!(
wrapped_display.ends_with(&underlying_display),
"wrapped Display `{wrapped_display}` must end with underlying Display `{underlying_display}`"
);
assert!(
wrapped_display.starts_with("source: "),
"wrapped Display `{wrapped_display}` must start with `\"<ctx>: \"`"
);
}
#[test]
fn err_ctx_admits_thiserror_derived_tagged_union_error() {
use crate::export::{ArtifactError, ARTIFACT_KIND_LIST};
let raw: Result<(), ArtifactError> = Err(ArtifactError::Empty(ARTIFACT_KIND_LIST));
let wrapped = raw.err_ctx("source").unwrap_err();
let wrapped_display = format!("{wrapped}");
assert!(
wrapped_display.starts_with("source: "),
"wrapped Display `{wrapped_display}` must start with `\"source: \"`"
);
assert!(
wrapped_display.contains(ARTIFACT_KIND_LIST),
"wrapped Display `{wrapped_display}` must thread ArtifactError's Display body verbatim"
);
}
#[test]
fn err_ctx_agrees_with_flatten_ctx_on_anyhow_result() {
use crate::anyhow_flatten::FlattenCtxExt;
let a: anyhow::Result<()> = Err(anyhow::anyhow!("underlying failure"));
let b: anyhow::Result<()> = Err(anyhow::anyhow!("underlying failure"));
assert_eq!(
format!("{}", a.err_ctx("source").unwrap_err()),
format!("{}", b.flatten_ctx("source").unwrap_err()),
"generic err_ctx and specialized flatten_ctx must agree on anyhow::Result"
);
}
}