zrx_executor/executor/task/tasks.rs
1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Task set.
27
28use std::vec::IntoIter;
29
30use super::Task;
31
32// ----------------------------------------------------------------------------
33// Structs
34// ----------------------------------------------------------------------------
35
36/// Task set.
37///
38/// This data type represents a set of tasks that can either be consumed through
39/// iteration, or executed recursively via [`Tasks::execute`]. Anything returned
40/// by [`Task::execute`] must be convertible into [`Tasks`], including a single
41/// task, multiple tasks, and the unit value.
42///
43/// # Examples
44///
45/// ```
46/// # use std::error::Error;
47/// # fn main() -> Result<(), Box<dyn Error>> {
48/// use zrx_executor::task::Tasks;
49/// use zrx_executor::Executor;
50///
51/// // Create executor and submit task
52/// let executor = Executor::default();
53/// executor.submit(|| {
54/// println!("Task 1");
55///
56/// // Create subtasks
57/// let mut tasks = Tasks::new();
58/// tasks.add(|| println!("Task 1.1"));
59/// tasks.add(|| println!("Task 1.2"));
60/// tasks.add(|| println!("Task 1.3"));
61/// tasks
62/// })?;
63/// # Ok(())
64/// # }
65/// ```
66#[derive(Debug, Default)]
67pub struct Tasks {
68 /// Vector of tasks.
69 inner: Vec<Box<dyn Task>>,
70}
71
72// ----------------------------------------------------------------------------
73// Implementations
74// ----------------------------------------------------------------------------
75
76impl Tasks {
77 /// Creates a task set.
78 ///
79 /// # Examples
80 ///
81 /// ```
82 /// use zrx_executor::task::Tasks;
83 ///
84 /// // Create task set
85 /// let tasks = Tasks::new();
86 /// ```
87 #[must_use]
88 pub fn new() -> Self {
89 Self::default()
90 }
91
92 /// Adds a task to the task set.
93 ///
94 /// This method adds a [`Task`] to the set, which can either be consumed
95 /// via [`Tasks::into_iter`] or executed via [`Tasks::execute`].
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use zrx_executor::task::Tasks;
101 ///
102 /// // Create task set and add tasks
103 /// let mut tasks = Tasks::new();
104 /// tasks.add(|| println!("Task 1"));
105 /// tasks.add(|| println!("Task 2"));
106 /// tasks.add(|| println!("Task 3"));
107 /// ```
108 #[inline]
109 pub fn add<T>(&mut self, task: T) -> &mut Self
110 where
111 T: Task,
112 {
113 self.inner.push(Box::new(task));
114 self
115 }
116
117 /// Executes all tasks in the task set.
118 ///
119 /// This method executes all tasks recursively in depth-first order, so if
120 /// a task returns further subtasks, they are executed before all others.
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use zrx_executor::task::Tasks;
126 ///
127 /// // Create task set and add tasks
128 /// let mut tasks = Tasks::new();
129 /// tasks.add(|| println!("Task 1"));
130 /// tasks.add(|| println!("Task 2"));
131 /// tasks.add(|| println!("Task 3"));
132 ///
133 /// // Execute task set
134 /// tasks.execute();
135 /// ```
136 pub fn execute(mut self) {
137 // Since we're using the inner vector as a stack, we need to reverse it
138 // to ensure that the first task added is the first one executed
139 self.inner.reverse();
140 while let Some(task) = self.inner.pop() {
141 // Execute the current task, and if it returns further subtasks,
142 // push them onto the stack in reverse order
143 self.inner.extend(task.execute().into_iter().rev());
144 }
145 }
146}
147
148#[allow(clippy::must_use_candidate)]
149impl Tasks {
150 /// Returns the number of tasks.
151 #[inline]
152 pub fn len(&self) -> usize {
153 self.inner.len()
154 }
155
156 /// Returns whether there are any tasks.
157 #[inline]
158 pub fn is_empty(&self) -> bool {
159 self.inner.is_empty()
160 }
161}
162
163// ----------------------------------------------------------------------------
164// Trait implementations
165// ----------------------------------------------------------------------------
166
167impl From<()> for Tasks {
168 /// Creates a task set from the unit value.
169 ///
170 /// This implementation makes the API more flexible, as it allows to just
171 /// return nothing from a task, which is probably the common case.
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// use zrx_executor::task::Tasks;
177 ///
178 /// // Create task set from unit value
179 /// let tasks = Tasks::from(());
180 /// assert!(tasks.is_empty());
181 /// ```
182 #[inline]
183 fn from((): ()) -> Self {
184 Self::default()
185 }
186}
187
188impl<T> From<T> for Tasks
189where
190 T: Task,
191{
192 /// Creates a task set from a task.
193 ///
194 /// This implementation creates a task set from a single task, which allows
195 /// to conveniently return a single closure from a task.
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// use zrx_executor::task::Tasks;
201 ///
202 /// // Create task set from task
203 /// let tasks = Tasks::from(|| println!("Task"));
204 /// assert_eq!(tasks.len(), 1);
205 /// ```
206 #[inline]
207 fn from(task: T) -> Self {
208 Self::from_iter([task])
209 }
210}
211
212// ----------------------------------------------------------------------------
213
214impl<I> FromIterator<I> for Tasks
215where
216 I: Task,
217{
218 /// Creates a task set from an iterator.
219 ///
220 /// # Examples
221 ///
222 /// ```
223 /// use zrx_executor::task::Tasks;
224 ///
225 /// // Create task set from iterator
226 /// let tasks = Tasks::from_iter([
227 /// || println!("Task 1"),
228 /// || println!("Task 2"),
229 /// || println!("Task 3"),
230 /// ]);
231 /// ```
232 #[inline]
233 fn from_iter<T>(iter: T) -> Self
234 where
235 T: IntoIterator<Item = I>,
236 {
237 let mut tasks = Self::new();
238 for task in iter {
239 tasks.add(task);
240 }
241 tasks
242 }
243}
244
245impl IntoIterator for Tasks {
246 type Item = Box<dyn Task>;
247 type IntoIter = IntoIter<Self::Item>;
248
249 /// Creates a consuming iterator over the task set.
250 ///
251 /// # Examples
252 ///
253 /// ```
254 /// use zrx_executor::task::Tasks;
255 ///
256 /// // Create task set and add tasks
257 /// let mut tasks = Tasks::new();
258 /// tasks.add(|| println!("Task 1"));
259 /// tasks.add(|| println!("Task 2"));
260 /// tasks.add(|| println!("Task 3"));
261 ///
262 /// // Create iterator over tasks
263 /// for task in tasks {
264 /// task.execute();
265 /// }
266 /// ```
267 #[inline]
268 fn into_iter(self) -> Self::IntoIter {
269 self.inner.into_iter()
270 }
271}