anyhow_auto_context/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
#![doc = include_str!("../README.md")]
/// Adds automatic `anyhow` context based on scope and location
///
/// ```
/// # use anyhow_auto_context::auto_context;
///
/// let expected_some: Option<()> = None;
/// let result = auto_context!(expected_some);
/// assert!(result.unwrap_err().to_string().starts_with("expected_some in"));
///
/// let expected_ok: anyhow::Result<()> = Err(anyhow::anyhow!("foo_err"));
/// let result = auto_context!(expected_ok);
/// assert!(result.unwrap_err().to_string().starts_with("expected_ok in"));
/// ```
#[macro_export]
macro_rules! auto_context {
($result:expr_2021) => {
anyhow::Context::with_context($result, || {
const fn f() {}
fn type_name<T>(_: T) -> &'static str {
::std::any::type_name::<T>()
}
let scope = type_name(f)
.strip_suffix("::f")
.unwrap_or_default()
.trim_end_matches("::{{closure}}");
format!(
"{} in {} at {}:{}:{}",
stringify!($result),
scope,
file!(),
line!(),
column!(),
)
})
};
}
#[cfg(test)]
mod tests {
use super::*;
fn ensure_42(n: i32) -> anyhow::Result<()> {
anyhow::ensure!(n == 42, "Expected 42");
Ok(())
}
#[test]
fn ok() {
assert!(auto_context!(ensure_42(42)).is_ok());
}
#[test]
fn err() {
let err_str = auto_context!(ensure_42(0)).unwrap_err().to_string();
assert!(err_str.starts_with("ensure_42(0) in"), "{err_str}");
}
#[test]
fn some() {
assert_eq!(auto_context!(Some(42)).unwrap(), 42);
}
#[test]
fn none() {
let expected_some: Option<i32> = None;
let err_str = auto_context!(expected_some).unwrap_err().to_string();
assert!(err_str.starts_with("expected_some in"), "{err_str}");
}
}