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}