1#![allow(missing_docs)]
4
5use crate::context::Context;
6
7#[macro_export]
8macro_rules! info {
9 ($ctx:expr, $msg:expr) => {
10 info!($ctx, $msg,)
11 };
12 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
13 let formatted = format!($msg, $($args),*);
14 let full = format!("{file}:{line}: {msg}",
15 file = file!(),
16 line = line!(),
17 msg = &formatted);
18 $ctx.emit_event($crate::EventType::Info(full));
19 }};
20}
21
22#[macro_export]
23macro_rules! warn {
24 ($ctx:expr, $msg:expr) => {
25 warn!($ctx, $msg,)
26 };
27 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
28 let formatted = format!($msg, $($args),*);
29 let full = format!("{file}:{line}: {msg}",
30 file = file!(),
31 line = line!(),
32 msg = &formatted);
33 $ctx.emit_event($crate::EventType::Warning(full));
34 }};
35}
36
37#[macro_export]
38macro_rules! error {
39 ($ctx:expr, $msg:expr) => {
40 error!($ctx, $msg,)
41 };
42 ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {{
43 let formatted = format!($msg, $($args),*);
44 $ctx.set_last_error(&formatted);
45 $ctx.emit_event($crate::EventType::Error(formatted));
46 }};
47}
48
49impl Context {
50 pub fn set_last_error(&self, error: &str) {
53 let mut last_error = self.last_error.write();
54 *last_error = error.to_string();
55 }
56
57 pub fn get_last_error(&self) -> String {
59 let last_error = &*self.last_error.read();
60 last_error.clone()
61 }
62}
63
64pub trait LogExt<T, E>
65where
66 Self: std::marker::Sized,
67{
68 #[track_caller]
79 fn log_err(self, context: &Context) -> Result<T, E>;
80}
81
82impl<T, E: std::fmt::Display> LogExt<T, E> for Result<T, E> {
83 #[track_caller]
84 fn log_err(self, context: &Context) -> Result<T, E> {
85 if let Err(e) = &self {
86 let location = std::panic::Location::caller();
87
88 let full = format!(
90 "{file}:{line}: {e:#}",
91 file = location.file(),
92 line = location.line(),
93 e = e
94 );
95 context.emit_event(crate::EventType::Warning(full));
98 };
99 self
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use anyhow::Result;
106
107 use crate::test_utils::TestContext;
108
109 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
110 async fn test_get_last_error() -> Result<()> {
111 let t = TestContext::new().await;
112
113 assert_eq!(t.get_last_error(), "");
114
115 error!(t, "foo-error");
116 assert_eq!(t.get_last_error(), "foo-error");
117
118 warn!(t, "foo-warning");
119 assert_eq!(t.get_last_error(), "foo-error");
120
121 info!(t, "foo-info");
122 assert_eq!(t.get_last_error(), "foo-error");
123
124 error!(t, "bar-error");
125 error!(t, "baz-error");
126 assert_eq!(t.get_last_error(), "baz-error");
127
128 Ok(())
129 }
130}