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