use hojicha_core::error::Error;
use hojicha_core::event::Event;
use std::sync::mpsc::SyncSender;
pub trait ErrorHandler<M>: Send + Sync {
fn handle_error(&self, error: Error, tx: &SyncSender<Event<M>>);
}
pub struct DefaultErrorHandler;
impl<M> ErrorHandler<M> for DefaultErrorHandler {
fn handle_error(&self, error: Error, _tx: &SyncSender<Event<M>>) {
eprintln!("Command execution error: {}", error);
let mut current_error: &dyn std::error::Error = &error;
while let Some(source) = current_error.source() {
eprintln!(" Caused by: {}", source);
current_error = source;
}
}
}
pub struct EventErrorHandler<M, F>
where
M: Clone + Send + 'static,
F: Fn(Error) -> M + Send + Sync,
{
converter: F,
}
impl<M, F> EventErrorHandler<M, F>
where
M: Clone + Send + 'static,
F: Fn(Error) -> M + Send + Sync,
{
pub fn new(converter: F) -> Self {
Self { converter }
}
}
impl<M, F> ErrorHandler<M> for EventErrorHandler<M, F>
where
M: Clone + Send + 'static,
F: Fn(Error) -> M + Send + Sync,
{
fn handle_error(&self, error: Error, tx: &SyncSender<Event<M>>) {
let msg = (self.converter)(error);
let _ = tx.send(Event::User(msg));
}
}
pub struct CompositeErrorHandler<M> {
handlers: Vec<Box<dyn ErrorHandler<M>>>,
}
impl<M> Default for CompositeErrorHandler<M> {
fn default() -> Self {
Self::new()
}
}
impl<M> CompositeErrorHandler<M> {
pub fn new() -> Self {
Self {
handlers: Vec::new(),
}
}
pub fn add_handler(mut self, handler: Box<dyn ErrorHandler<M>>) -> Self {
self.handlers.push(handler);
self
}
}
impl<M> ErrorHandler<M> for CompositeErrorHandler<M> {
fn handle_error(&self, error: Error, tx: &SyncSender<Event<M>>) {
let error_string = error.to_string();
for handler in &self.handlers {
handler.handle_error(Error::Model(error_string.clone()), tx);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::mpsc;
#[derive(Clone, Debug, PartialEq)]
enum TestMsg {
Error(String),
}
#[test]
fn test_default_error_handler() {
let (tx, _rx) = mpsc::sync_channel::<Event<TestMsg>>(10);
let handler = DefaultErrorHandler;
let error = Error::Model("Test error".to_string());
handler.handle_error(error, &tx);
}
#[test]
fn test_event_error_handler() {
let (tx, rx) = mpsc::sync_channel(10);
let handler = EventErrorHandler::new(|err| TestMsg::Error(err.to_string()));
let error = Error::Model("Test error".to_string());
handler.handle_error(error, &tx);
let event = rx.recv().unwrap();
assert_eq!(
event,
Event::User(TestMsg::Error("Model error: Test error".to_string()))
);
}
#[test]
fn test_composite_error_handler() {
let (tx, rx) = mpsc::sync_channel(10);
let composite = CompositeErrorHandler::new()
.add_handler(Box::new(DefaultErrorHandler))
.add_handler(Box::new(EventErrorHandler::new(|err| {
TestMsg::Error(format!("Handled: {}", err))
})));
let error = Error::Model("Test error".to_string());
composite.handle_error(error, &tx);
let event = rx.recv().unwrap();
assert_eq!(
event,
Event::User(TestMsg::Error(
"Handled: Model error: Model error: Test error".to_string()
))
);
}
}