anyhow_loc/
lib.rs

1use std::fmt::Display;
2
3pub struct Location {
4    pub file: &'static str,
5    pub line: u32,
6    pub msg: String,
7}
8
9pub trait ErrorLocation<T, E>
10where
11    E: Display,
12    Result<T, E>: anyhow::Context<T, E>,
13{
14    /// Used with `msg!` macro
15    fn location(self, msg: Location) -> anyhow::Result<T>;
16
17    /// Used with `msg!` macro
18    fn with_location<F>(self, f: F) -> anyhow::Result<T>
19    where
20        F: FnOnce() -> Location;
21}
22
23impl<T, E> ErrorLocation<T, E> for Result<T, E>
24where
25    E: Display,
26    Result<T, E>: anyhow::Context<T, E>,
27{
28    fn location(self, msg: Location) -> anyhow::Result<T> {
29        use anyhow::Context;
30        self.with_context(|| format!("{} \n at {}:{}", msg.msg, msg.file, msg.line,))
31    }
32
33    fn with_location<F>(self, f: F) -> anyhow::Result<T>
34    where
35        F: FnOnce() -> Location,
36    {
37        use anyhow::Context;
38        let msg = f();
39        self.with_context(|| format!("{} \n at {}:{}", msg.msg, msg.file, msg.line,))
40    }
41}
42
43#[macro_export]
44macro_rules! msg {
45    ($($arg:tt)*) => {
46        $crate::Location {
47            file: file!(),
48            line: line!(),
49            msg: format!($($arg)*),
50        }
51    };
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    fn f() -> anyhow::Result<()> {
59        anyhow::bail!("oh no!");
60    }
61
62    #[test]
63    #[should_panic(expected = "error 2")]
64    fn it_location() {
65        let t = String::from("error 2");
66        f().location(msg!("error 1")) // t
67            .location(msg!("{}", t))
68            .unwrap();
69    }
70
71    #[test]
72    #[should_panic(expected = "error 2")]
73    fn it_with_location() {
74        let t = String::from("error 2");
75        f().with_location(|| msg!("error 1")) // t
76            .with_location(|| msg!("{}", t))
77            .unwrap();
78    }
79}