use std::{backtrace::BacktraceStatus, collections::VecDeque, sync::Arc};
use anyhow::Error;
use egui::{Color32, Context, mutex::RwLock};
use egui_notify::{Toast, Toasts};
use log::error;
#[derive(Default, Clone)]
pub(crate) struct Notifier {
toasts: Arc<RwLock<Toasts>>,
error_queue: Arc<RwLock<VecDeque<Error>>>,
}
impl Notifier {
pub(crate) fn report_error(&self, e: Error) {
let error_msg = if e.chain().len() > 1 {
format!("{e}: {}", e.root_cause())
} else {
format!("{e}")
};
let backtrace = e.backtrace();
if backtrace.status() == BacktraceStatus::Captured {
error!("{error_msg}\n{backtrace}");
} else {
error!("{error_msg}");
}
let mut error_queue = self.error_queue.write();
error_queue.push_back(e);
}
pub(crate) fn ok_or_report<T>(&self, result: anyhow::Result<T>) -> Option<T> {
match result {
Ok(v) => Some(v),
Err(err) => {
self.report_error(err);
None
}
}
}
pub(crate) fn unwrap_or_default<T>(&self, result: anyhow::Result<T>) -> T
where
T: Default,
{
match result {
Ok(o) => o,
Err(e) => {
self.report_error(e);
T::default()
}
}
}
pub(crate) fn add_toast(&self, toast: Toast) {
let mut messages = self.toasts.write();
messages.add(toast);
}
pub(super) fn show(&self, ctx: &Context) {
let mut messages = self.toasts.write();
let mut error_queue = self.error_queue.write();
while let Some(e) = error_queue.pop_front() {
let error_msg = if e.chain().len() > 1 {
format!("{e}: {}", e.root_cause())
} else {
format!("{e}")
};
let mut toast = Toast::custom(
error_msg,
egui_notify::ToastLevel::Custom(
egui_phosphor::regular::EXCLAMATION_MARK.to_string(),
Color32::from_rgb(200, 90, 90),
),
);
toast.closable(true);
toast.show_progress_bar(false);
toast.duration(None);
messages.add(toast);
}
messages.show(ctx);
}
#[cfg(test)]
pub(crate) fn is_empty(&self) -> bool {
let messages = self.toasts.read();
messages.is_empty()
}
}
#[cfg(test)]
mod tests {
use crate::app::tests::{create_app_with_corpus, create_test_harness};
#[test]
fn show_error_message() {
let app_state = create_app_with_corpus(
"single_sentence",
&include_bytes!("../../tests/data/single_sentence.graphml")[..],
);
let notifier = app_state.notifier.clone();
let (mut harness, _app_state) = create_test_harness(app_state);
harness.run();
notifier.report_error(anyhow::anyhow!("Test error"));
harness.run();
harness.snapshot("show_error_message");
}
}