1use std::{
2 fmt::Debug,
3 sync::{Arc, Mutex},
4};
5
6use tracing::error;
7
8use crate::jobs::{JobDefinition, JobId};
9
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12 #[error(transparent)]
13 Redis(#[from] redis::RedisError),
14 #[error(transparent)]
15 Serialization(#[from] bincode::Error),
16 #[error("handler already registered for name '{0}'")]
17 HandlerAlreadyRegistered(&'static str),
18 #[error("handler not found for job {0:?}")]
19 NoHandler(JobDefinition),
20 #[error("job {} ({}) failed: {}", .0.job_name, .0.id, .1)]
21 JobFailed(JobDefinition, #[source] StdError),
22 #[error("job failures: {0:?}")]
23 JobFailures(Vec<Error>),
24}
25
26pub type Result<T> = std::result::Result<T, Error>;
27
28pub(crate) type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
29
30pub trait ErrorHandler: Send + Sync {
31 fn job_failed(&self, job_id: &JobId, job_name: &str, error: &Error);
32}
33
34#[derive(Clone, Default)]
35pub struct ErrorHandlers {
36 handlers: Arc<Mutex<Vec<Box<dyn ErrorHandler>>>>,
37}
38
39impl std::fmt::Debug for ErrorHandlers {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.debug_struct("ErrorHandlers").finish()
42 }
43}
44
45impl ErrorHandlers {
46 pub(crate) fn add(&mut self, handler: impl ErrorHandler + 'static) {
47 self.handlers.lock().unwrap().push(Box::new(handler));
48 }
49}
50
51impl ErrorHandler for ErrorHandlers {
52 fn job_failed(&self, job_id: &JobId, job_name: &str, error: &Error) {
53 match self.handlers.lock() {
54 Ok(handlers) => {
55 for handler in handlers.iter() {
56 handler.job_failed(job_id, job_name, error);
57 }
58 }
59 Err(e) => {
60 error!("failed to lock handlers: {}", e);
61 }
62 }
63 }
64}