qubit_executor/task/task_runner.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9use std::panic::{
10 AssertUnwindSafe,
11 catch_unwind,
12};
13
14use qubit_function::Callable;
15
16use super::{
17 TaskCompletion,
18 TaskExecutionError,
19 TaskResult,
20};
21
22/// Runner that executes a callable task with standard task-handle semantics.
23///
24/// `TaskRunner` owns the accepted callable, converts task failures and panics
25/// into [`TaskExecutionError`], and can publish the final result through a
26/// [`TaskCompletion`] endpoint.
27pub struct TaskRunner<C> {
28 /// Callable task owned by this runner.
29 task: C,
30}
31
32impl<C> TaskRunner<C> {
33 /// Creates a runner for the supplied callable task.
34 ///
35 /// # Parameters
36 ///
37 /// * `task` - Callable task to execute later.
38 ///
39 /// # Returns
40 ///
41 /// A runner that owns the callable task.
42 #[inline]
43 pub const fn new(task: C) -> Self {
44 Self { task }
45 }
46
47 /// Runs the callable and converts task failure and panic into a handle result.
48 ///
49 /// # Returns
50 ///
51 /// `Ok(R)` if the task succeeds. If the task returns `Err(E)` or panics, the
52 /// corresponding [`TaskExecutionError`] is returned.
53 pub fn call<R, E>(self) -> TaskResult<R, E>
54 where
55 C: Callable<R, E>,
56 {
57 let mut task = self.task;
58 match catch_unwind(AssertUnwindSafe(|| task.call())) {
59 Ok(Ok(value)) => Ok(value),
60 Ok(Err(err)) => Err(TaskExecutionError::Failed(err)),
61 Err(_) => Err(TaskExecutionError::Panicked),
62 }
63 }
64
65 /// Runs this task through a task completion endpoint.
66 ///
67 /// # Parameters
68 ///
69 /// * `completion` - Completion endpoint that publishes the final result.
70 ///
71 /// # Returns
72 ///
73 /// `true` if the task started and its result was published, or `false` if
74 /// the completion endpoint had already been completed by cancellation.
75 #[inline]
76 pub fn run<R, E>(self, completion: TaskCompletion<R, E>) -> bool
77 where
78 C: Callable<R, E>,
79 {
80 completion.start_and_complete(|| self.call())
81 }
82}