apalis_core/task/
builder.rs

1//! # Task Builder
2//!
3//! The `TaskBuilder` module provides a flexible builder pattern for constructing [`Task`] instances
4//! with customizable configuration options. It allows users to specify arguments, context, extensions,
5//! task identifiers, attempt information, status, and scheduling details for tasks.
6//!
7//! ## Features
8//! - Create tasks with required arguments and optional context.
9//! - Attach custom extensions/data to tasks.
10//! - Assign unique task identifiers.
11//! - Configure attempt and status information.
12//! - Schedule tasks to run at specific times, after delays, or at intervals (seconds, minutes, hours).
13//! - Build tasks with sensible defaults for omitted fields.
14//!
15//! ## Usage
16//! Use [`TaskBuilder`] to incrementally configure a task, then call `.build()` to obtain a [`Task`] instance.
17//! Convenience methods are provided for common scheduling scenarios.
18//!
19//! ### Example
20//! ```rust,ignore
21//! let task = TaskBuilder::new(args)
22//!     .attempts(3)
23//!     .run_in_minutes(10)
24//!     .build();
25//! ```
26//!
27use crate::task::{
28    attempt::Attempt, extensions::Extensions, metadata::MetadataExt, status::Status,
29    task_id::TaskId, Parts, Task,
30};
31use std::time::{Duration, SystemTime, UNIX_EPOCH};
32
33/// Builder for creating [`Task`] instances with optional configuration
34#[derive(Debug)]
35pub struct TaskBuilder<Args, Ctx, IdType> {
36    pub(super) args: Args,
37    pub(super) ctx: Ctx,
38    pub(super) data: Extensions,
39    pub(super) task_id: Option<TaskId<IdType>>,
40    pub(super) attempt: Option<Attempt>,
41    pub(super) status: Option<Status>,
42    pub(super) run_at: Option<u64>,
43}
44
45impl<Args, Ctx, IdType> TaskBuilder<Args, Ctx, IdType> {
46    /// Create a new TaskBuilder with the required args
47    pub fn new(args: Args) -> Self
48    where
49        Ctx: Default,
50    {
51        Self {
52            args,
53            ctx: Default::default(),
54            data: Extensions::default(),
55            task_id: None,
56            attempt: None,
57            status: None,
58            run_at: None,
59        }
60    }
61
62    /// Set the task's backend context
63    pub fn with_ctx(mut self, ctx: Ctx) -> Self {
64        self.ctx = ctx;
65        self
66    }
67
68    /// Set the task's runtime data
69    pub fn with_data(mut self, data: Extensions) -> Self {
70        self.data = data;
71        self
72    }
73
74    /// Insert a value into the task's data context
75    pub fn data<D: Clone + Send + Sync + 'static>(mut self, value: D) -> Self {
76        self.data.insert(value);
77        self
78    }
79
80    /// Insert a value into the task's ctx context
81    pub fn meta<M>(mut self, value: M) -> Self
82    where
83        Ctx: MetadataExt<M>,
84    {
85        self.ctx
86            .inject(value)
87            .unwrap_or_else(|_| panic!("Failed to inject item into context"));
88        self
89    }
90
91    /// Set the task ID
92    pub fn with_task_id(mut self, task_id: TaskId<IdType>) -> Self {
93        self.task_id = Some(task_id);
94        self
95    }
96
97    /// Set the attempt information
98    pub fn with_attempt(mut self, attempt: Attempt) -> Self {
99        self.attempt = Some(attempt);
100        self
101    }
102
103    /// Set the task status
104    pub fn with_status(mut self, status: Status) -> Self {
105        self.status = Some(status);
106        self
107    }
108
109    /// Schedule the task to run at a specific Unix timestamp
110    pub fn run_at_timestamp(mut self, timestamp: u64) -> Self {
111        self.run_at = Some(timestamp);
112        self
113    }
114
115    /// Schedule the task to run at a specific SystemTime
116    pub fn run_at_time(mut self, time: SystemTime) -> Self {
117        let timestamp = time
118            .duration_since(UNIX_EPOCH)
119            .expect("Time went backwards")
120            .as_secs();
121        self.run_at = Some(timestamp);
122        self
123    }
124
125    /// Schedule the task to run after a delay from now
126    pub fn run_after(mut self, delay: Duration) -> Self {
127        let now = SystemTime::now();
128        let run_time = now + delay;
129        let timestamp = run_time
130            .duration_since(UNIX_EPOCH)
131            .expect("Time went backwards")
132            .as_secs();
133        self.run_at = Some(timestamp);
134        self
135    }
136
137    /// Schedule the task to run in the specified number of seconds
138    pub fn run_in_seconds(self, seconds: u64) -> Self {
139        self.run_after(Duration::from_secs(seconds))
140    }
141
142    /// Schedule the task to run in the specified number of minutes
143    pub fn run_in_minutes(self, minutes: u64) -> Self {
144        self.run_after(Duration::from_secs(minutes * 60))
145    }
146
147    /// Schedule the task to run in the specified number of hours
148    pub fn run_in_hours(self, hours: u64) -> Self {
149        self.run_after(Duration::from_secs(hours * 3600))
150    }
151
152    /// Build the Task with default context
153    pub fn build(self) -> Task<Args, Ctx, IdType> {
154        let current_time = || {
155            SystemTime::now()
156                .duration_since(UNIX_EPOCH)
157                .expect("Time went backwards")
158                .as_secs()
159        };
160
161        Task {
162            args: self.args,
163            parts: Parts {
164                task_id: self.task_id,
165                data: self.data,
166                attempt: self.attempt.unwrap_or_default(),
167                ctx: self.ctx,
168                status: self.status.unwrap_or(Status::Pending),
169                run_at: self.run_at.unwrap_or_else(current_time),
170            },
171        }
172    }
173}
174
175// Convenience methods for Task to create a builder
176impl<Args, Ctx: Default, IdType> Task<Args, Ctx, IdType> {
177    /// Create a TaskBuilder with the given args
178    pub fn builder(args: Args) -> TaskBuilder<Args, Ctx, IdType> {
179        TaskBuilder::new(args)
180    }
181}