use std::{
fmt::Debug,
sync::{Arc, Mutex},
};
use tracing::error;
use crate::jobs::{JobDefinition, JobId};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Redis(#[from] redis::RedisError),
#[error(transparent)]
Serialization(#[from] bincode::Error),
#[error("handler already registered for name '{0}'")]
HandlerAlreadyRegistered(&'static str),
#[error("handler not found for job {0:?}")]
NoHandler(JobDefinition),
#[error("job {} ({}) failed: {}", .0.job_name, .0.id, .1)]
JobFailed(JobDefinition, #[source] StdError),
#[error("job failures: {0:?}")]
JobFailures(Vec<Error>),
}
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub trait ErrorHandler: Send + Sync {
fn job_failed(&self, job_id: &JobId, job_name: &str, error: &Error);
}
#[derive(Clone, Default)]
pub struct ErrorHandlers {
handlers: Arc<Mutex<Vec<Box<dyn ErrorHandler>>>>,
}
impl std::fmt::Debug for ErrorHandlers {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ErrorHandlers").finish()
}
}
impl ErrorHandlers {
pub(crate) fn add(&mut self, handler: impl ErrorHandler + 'static) {
self.handlers.lock().unwrap().push(Box::new(handler));
}
}
impl ErrorHandler for ErrorHandlers {
fn job_failed(&self, job_id: &JobId, job_name: &str, error: &Error) {
match self.handlers.lock() {
Ok(handlers) => {
for handler in handlers.iter() {
handler.job_failed(job_id, job_name, error);
}
}
Err(e) => {
error!("failed to lock handlers: {}", e);
}
}
}
}