zirv_queue/traits/mod.rs
1use chrono::NaiveDateTime;
2use std::error::Error;
3
4use crate::{
5 models::job::{Job as JobModel, NewJob},
6 utils::serialize,
7};
8
9/// A trait representing a Queueable struct in the system.
10///
11/// This trait uses [`typetag::serde`] to enable dynamic serialization/deserialization,
12/// allowing different job types to be stored or retrieved from a database while
13/// preserving their concrete types at runtime.
14#[typetag::serde]
15pub trait Queueable {
16 /// Called when a worker reserves this job and needs to process it.
17 ///
18 /// # Returns
19 ///
20 /// Returns `Ok(())` if processing completes successfully. Otherwise, returns
21 /// an error describing the issue encountered.
22 ///
23 /// # Example
24 ///
25 /// ```rust,ignore
26 /// struct MyJob {
27 /// // fields...
28 /// }
29 ///
30 /// impl Queueable for MyJob {
31 /// fn handle(&self) -> Result<Option<String>, Box<dyn std::error::Error>> {
32 /// // custom logic here
33 /// Ok(())
34 /// }
35 /// }
36 /// ```
37 fn handle(&self) -> Result<Option<String>, Box<dyn std::error::Error>>;
38
39 /// Queues this job for immediate processing by inserting it into the jobs table.
40 ///
41 /// Implementation notes:
42 /// - The job is serialized via the [`serialize::serialize`] function.
43 /// - A new [`NewJob`] instance is created with the current time as `available_at`.
44 /// - The [`JobModel::create`] method is then used to insert the job into the database.
45 ///
46 /// # Returns
47 ///
48 /// - `Ok(())` if the job was successfully serialized and inserted.
49 /// - `Err(Box<dyn Error>)` if serialization fails or the insert operation encounters an error.
50 ///
51 /// # Constraints
52 ///
53 /// - This method requires `Self: Sized` because it needs to create a new [`NewJob`]
54 /// based on the concrete type implementing this trait.
55 fn dispatch(&self) -> Result<(), Box<dyn Error>>
56 where
57 Self: std::marker::Sized,
58 {
59 let payload = match serialize::serialize(self) {
60 Ok(p) => p,
61 Err(e) => return Err(Box::new(e)),
62 };
63
64 let job = NewJob::new(payload);
65
66 match JobModel::create(job) {
67 Ok(_) => Ok(()),
68 Err(e) => Err(Box::new(e)),
69 }
70 }
71
72 /// Schedules this job to be available at a specified future time, instead of right away.
73 ///
74 /// Similar to [`dispatch`](Self::dispatch), but sets a custom `available_at` timestamp,
75 /// delaying when the job becomes eligible for processing.
76 ///
77 /// # Parameters
78 ///
79 /// - `at_time`: The timestamp after which this job can be processed.
80 ///
81 /// # Returns
82 ///
83 /// - `Ok(())` if the job was successfully serialized and inserted into the database
84 /// with the specified availability time.
85 /// - `Err(Box<dyn Error>)` if serialization fails or the insertion encounters an error.
86 ///
87 /// # Example
88 ///
89 /// ```rust,ignore
90 /// let future_time = chrono::Local::now().naive_local() + chrono::Duration::minutes(30);
91 ///
92 /// my_job.schedule_at(future_time)
93 /// .expect("Failed to schedule the job for later processing");
94 /// ```
95 fn schedule_at(&self, at_time: NaiveDateTime) -> Result<(), Box<dyn Error>>
96 where
97 Self: std::marker::Sized,
98 {
99 let payload = match serialize::serialize(self) {
100 Ok(p) => p,
101 Err(e) => return Err(Box::new(e)),
102 };
103
104 let job = NewJob::with_time(payload, at_time);
105
106 match JobModel::create(job) {
107 Ok(_) => Ok(()),
108 Err(e) => Err(Box::new(e)),
109 }
110 }
111}