steady/
error.rs

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}