Skip to main content

moirai_core/task/
builder.rs

1use core::marker::PhantomData;
2
3use super::id_and_context::{TaskContext, TaskId};
4use super::traits::Task;
5
6// ── BaseTask ──────────────────────────────────────────────────────────────────
7
8/// Base implementation for common task patterns to reduce redundancy.
9pub struct BaseTask<F, R> {
10    pub(super) func: F,
11    pub(super) context: TaskContext,
12    pub(super) _phantom: PhantomData<R>,
13}
14
15impl<F, R> BaseTask<F, R>
16where
17    F: FnOnce() -> R + Send + 'static,
18    R: Send + 'static,
19{
20    /// Create a new base task with the given function and context.
21    pub fn new(func: F, context: TaskContext) -> Self {
22        Self {
23            func,
24            context,
25            _phantom: PhantomData,
26        }
27    }
28}
29
30// ── Closure ───────────────────────────────────────────────────────────────────
31
32/// A simple closure-based task implementation.
33pub struct Closure<F, R> {
34    base: BaseTask<F, R>,
35}
36
37impl<F, R> Closure<F, R>
38where
39    F: FnOnce() -> R + Send + 'static,
40    R: Send + 'static,
41{
42    /// Create a new closure task.
43    pub fn new(func: F, context: TaskContext) -> Self {
44        Self {
45            base: BaseTask::new(func, context),
46        }
47    }
48
49    /// Chain another operation after this task.
50    pub fn then<G, S>(self, continuation: G) -> Chained<Self, G>
51    where
52        G: FnOnce(R) -> S + Send + 'static,
53        S: Send + 'static,
54    {
55        Chained::new(self, continuation)
56    }
57
58    /// Map the output of this task.
59    pub fn map<G, S>(self, mapper: G) -> Mapped<Self, G>
60    where
61        G: FnOnce(R) -> S + Send + 'static,
62        S: Send + 'static,
63    {
64        Mapped::new(self, mapper)
65    }
66}
67
68impl<F, R> Task for Closure<F, R>
69where
70    F: FnOnce() -> R + Send + 'static,
71    R: Send + 'static,
72{
73    type Output = R;
74
75    fn execute(self) -> Self::Output {
76        (self.base.func)()
77    }
78
79    fn context(&self) -> &TaskContext {
80        &self.base.context
81    }
82}
83
84// ── Chained ───────────────────────────────────────────────────────────────────
85
86/// A task that chains two operations together.
87pub struct Chained<T, F> {
88    task: T,
89    continuation: F,
90    context: TaskContext,
91}
92
93impl<T, F> Chained<T, F> {
94    /// Create a new chained task.
95    pub fn new(task: T, continuation: F) -> Self
96    where
97        T: Task,
98    {
99        let _context = task.context().clone();
100        Self {
101            task,
102            continuation,
103            context: _context,
104        }
105    }
106}
107
108impl<T, F, U> Task for Chained<T, F>
109where
110    T: Task,
111    F: FnOnce(T::Output) -> U + Send + 'static,
112    U: Send + 'static,
113{
114    type Output = U;
115
116    fn execute(self) -> Self::Output {
117        let result = self.task.execute();
118        (self.continuation)(result)
119    }
120
121    fn context(&self) -> &TaskContext {
122        &self.context
123    }
124
125    fn is_stealable(&self) -> bool {
126        self.task.is_stealable()
127    }
128
129    fn estimated_cost(&self) -> u32 {
130        self.task.estimated_cost() + 1
131    }
132}
133
134// ── Mapped ────────────────────────────────────────────────────────────────────
135
136/// A task that maps the output of another task.
137pub struct Mapped<T, F> {
138    task: T,
139    mapper: F,
140    context: TaskContext,
141}
142
143impl<T, F> Mapped<T, F> {
144    /// Create a new mapped task.
145    pub fn new(task: T, mapper: F) -> Self
146    where
147        T: Task,
148    {
149        let _context = task.context().clone();
150        Self {
151            task,
152            mapper,
153            context: _context,
154        }
155    }
156}
157
158impl<T, F, U> Task for Mapped<T, F>
159where
160    T: Task,
161    F: FnOnce(T::Output) -> U + Send + 'static,
162    U: Send + 'static,
163{
164    type Output = U;
165
166    fn execute(self) -> Self::Output {
167        let result = self.task.execute();
168        (self.mapper)(result)
169    }
170
171    fn context(&self) -> &TaskContext {
172        &self.context
173    }
174
175    fn is_stealable(&self) -> bool {
176        self.task.is_stealable()
177    }
178
179    fn estimated_cost(&self) -> u32 {
180        self.task.estimated_cost()
181    }
182}
183
184// ── TaskBuilder ───────────────────────────────────────────────────────────────
185
186/// Builder for creating and configuring tasks.
187#[allow(clippy::module_name_repetitions)]
188pub struct TaskBuilder {
189    context: TaskContext,
190}
191
192impl TaskBuilder {
193    /// Creates a new task builder with default settings.
194    ///
195    /// # Returns
196    /// A new builder instance ready for configuration
197    #[must_use]
198    pub fn new() -> Self {
199        // Generate a dummy ID for now - this should be replaced by the executor
200        Self {
201            context: TaskContext::new(TaskId::new(0)),
202        }
203    }
204
205    /// Sets the priority level for the task.
206    ///
207    /// # Arguments
208    /// * `priority` - The scheduling priority for this task
209    ///
210    /// # Returns
211    /// The builder instance for method chaining
212    #[must_use]
213    pub fn priority(mut self, priority: crate::Priority) -> Self {
214        self.context.priority = priority;
215        self
216    }
217
218    /// Sets a descriptive name for the task.
219    ///
220    /// # Arguments
221    /// * `name` - A static string name for debugging and monitoring
222    ///
223    /// # Returns
224    /// The builder instance for method chaining
225    #[must_use]
226    pub fn name(mut self, name: &'static str) -> Self {
227        self.context.name = Some(name);
228        self
229    }
230
231    /// Sets the task ID and returns the modified task builder.
232    ///
233    /// # Arguments
234    /// * `id` - The unique identifier for this task
235    ///
236    /// # Returns
237    /// The task builder with the specified ID set
238    #[must_use]
239    pub fn with_id(mut self, id: TaskId) -> Self {
240        self.context.id = id;
241        self
242    }
243
244    /// Build the task with the provided function.
245    pub fn build<F, R>(self, func: F) -> Closure<F, R>
246    where
247        F: FnOnce() -> R + Send + 'static,
248        R: Send + 'static,
249    {
250        Closure::new(func, self.context)
251    }
252}
253
254impl Default for TaskBuilder {
255    fn default() -> Self {
256        Self::new()
257    }
258}
259
260// ── Parameterized ─────────────────────────────────────────────────────────────
261
262/// A task that accepts parameters for customized execution.
263///
264/// This provides a way to create reusable task templates that can
265/// be parameterized at execution time.
266pub struct Parameterized<F, P> {
267    /// The parameterized function to execute
268    function: Option<F>,
269    /// The parameters to pass to the function
270    parameters: Option<P>,
271    /// Task execution context and metadata
272    context: TaskContext,
273}
274
275impl<F, P> Parameterized<F, P> {
276    /// Create a new parameterized task.
277    pub fn new(func: F, params: P, context: TaskContext) -> Self {
278        Self {
279            function: Some(func),
280            parameters: Some(params),
281            context,
282        }
283    }
284}
285
286impl<F, P, R> Task for Parameterized<F, P>
287where
288    F: FnOnce(P) -> R + Send + 'static,
289    P: Send + 'static,
290    R: Send + 'static,
291{
292    type Output = R;
293
294    fn execute(mut self) -> Self::Output {
295        let func = self.function.take().expect("Task already executed");
296        let params = self.parameters.take().expect("Parameters already used");
297        func(params)
298    }
299
300    fn context(&self) -> &TaskContext {
301        &self.context
302    }
303}
304
305// ── Group ─────────────────────────────────────────────────────────────────────
306
307/// A collection of related tasks that can be executed as a group.
308///
309/// This provides batch execution capabilities and allows for
310/// coordinated task management and monitoring.
311pub struct Group {
312    /// Collection of tasks in this group
313    tasks: Vec<Box<dyn FnOnce() + Send + 'static>>,
314    /// Task execution context and metadata (carries the group's `TaskId`)
315    context: TaskContext,
316}
317
318impl Group {
319    /// Creates a new task group with the specified ID.
320    ///
321    /// # Arguments
322    /// * `id` - Unique identifier for the task group
323    ///
324    /// # Returns
325    /// A new empty task group
326    #[must_use]
327    pub fn new(id: TaskId) -> Self {
328        Self {
329            tasks: Vec::new(),
330            context: TaskContext::new(id),
331        }
332    }
333
334    /// Add a task to the group.
335    pub fn add_task<F>(&mut self, task_fn: F)
336    where
337        F: FnOnce() + Send + 'static,
338    {
339        self.tasks.push(Box::new(move || {
340            task_fn();
341        }));
342    }
343
344    /// Returns the number of tasks in this group.
345    ///
346    /// # Returns
347    /// The count of tasks currently in the group
348    #[must_use]
349    pub fn len(&self) -> usize {
350        self.tasks.len()
351    }
352
353    /// Checks if the task group is empty.
354    ///
355    /// # Returns
356    /// `true` if the group contains no tasks, `false` otherwise
357    #[must_use]
358    pub fn is_empty(&self) -> bool {
359        self.tasks.is_empty()
360    }
361}
362
363impl Task for Group {
364    type Output = ();
365
366    fn execute(self) -> Self::Output {
367        // Execute each task function
368        for task_fn in self.tasks {
369            task_fn();
370        }
371    }
372
373    fn context(&self) -> &TaskContext {
374        &self.context
375    }
376
377    #[allow(clippy::cast_possible_truncation)]
378    fn estimated_cost(&self) -> u32 {
379        self.tasks.len() as u32
380    }
381}
382
383// ── Spawner ───────────────────────────────────────────────────────────────────
384
385/// A task that can spawn other tasks during its execution.
386///
387/// This provides dynamic task creation capabilities, allowing tasks
388/// to generate additional work based on runtime conditions.
389pub struct Spawner<F> {
390    /// The spawning function that creates new tasks
391    spawner: Option<F>,
392    /// Task execution context and metadata
393    context: TaskContext,
394}
395
396impl<F> Spawner<F> {
397    /// Create a new spawner task.
398    pub fn new(spawner: F, context: TaskContext) -> Self {
399        Self {
400            spawner: Some(spawner),
401            context,
402        }
403    }
404}
405
406impl<F> Task for Spawner<F>
407where
408    F: FnOnce() + Send + 'static,
409{
410    type Output = ();
411
412    fn execute(mut self) -> Self::Output {
413        if let Some(spawner) = self.spawner.take() {
414            spawner();
415        }
416    }
417
418    fn context(&self) -> &TaskContext {
419        &self.context
420    }
421}