Skip to main content

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}