Skip to main content

queuey_core/
handler.rs

1//! [`JobHandler`], the [`JobContext`] it receives and the [`FnHandler`] adapter.
2
3use std::time::Duration;
4
5use async_trait::async_trait;
6use uuid::Uuid;
7
8use crate::{error::JobError, job::Job};
9
10/// Metadata about the current execution, available to handlers.
11#[derive(Debug, Clone)]
12pub struct JobContext {
13    /// Stable id of the job, unchanged across retries.
14    pub job_id: Uuid,
15    /// `Job::NAME` of the running job.
16    pub job_type: &'static str,
17    /// Broker queue name the job was consumed from.
18    pub queue: &'static str,
19    /// 1-based.
20    pub attempt: u32,
21    /// Total attempts the effective retry policy allows.
22    pub max_attempts: u32,
23    /// How often this job was deferred (see [`crate::JobError::Deferred`]).
24    ///
25    /// Independent of `attempt`. There is no built-in cap: a handler that wants one
26    /// checks this and returns [`crate::JobError::Fatal`] instead of deferring again.
27    pub deferrals: u32,
28    /// Broker message priority this delivery arrived with; `0` is normal work.
29    pub priority: u8,
30    /// Time since first enqueue.
31    pub age: Duration,
32}
33
34impl JobContext {
35    /// Whether a failure now means the job is dead-lettered.
36    pub fn is_last_attempt(&self) -> bool {
37        self.attempt >= self.max_attempts
38    }
39}
40
41/// Processes jobs of one type. Register with [`crate::WorkerBuilder::handler`].
42#[async_trait]
43pub trait JobHandler: Send + Sync + 'static {
44    /// The one job type this handler processes.
45    type Job: Job;
46
47    /// Process one job. Returning `Err` hands control to the retry policy.
48    async fn handle(&self, job: Self::Job, ctx: JobContext) -> Result<(), JobError>;
49}
50
51/// Blanket adapter so plain async closures can be handlers:
52/// `builder.handler(FnHandler::<SendEmail, _>::new(|job, ctx| async move { ... }))`.
53pub struct FnHandler<J, F> {
54    f: F,
55    _job: std::marker::PhantomData<fn(J)>,
56}
57
58impl<J, F> FnHandler<J, F> {
59    /// Wrap `f` as a [`JobHandler`] for job type `J`.
60    pub fn new(f: F) -> Self {
61        Self {
62            f,
63            _job: std::marker::PhantomData,
64        }
65    }
66}
67
68#[async_trait]
69impl<J, F, Fut> JobHandler for FnHandler<J, F>
70where
71    J: Job,
72    F: Fn(J, JobContext) -> Fut + Send + Sync + 'static,
73    Fut: std::future::Future<Output = Result<(), JobError>> + Send + 'static,
74{
75    type Job = J;
76
77    async fn handle(&self, job: J, ctx: JobContext) -> Result<(), JobError> {
78        (self.f)(job, ctx).await
79    }
80}