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}