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 fn location(self, msg: Location) -> anyhow::Result<T>;
16
17 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")) .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")) .with_location(|| msg!("{}", t))
77 .unwrap();
78 }
79}