qubit_function/tasks/callable.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # Callable Types
10//!
11//! Provides fallible, one-time, zero-argument computations.
12//!
13//! A `Callable<R, E>` is equivalent to `FnOnce() -> Result<R, E>`, but uses
14//! task-oriented vocabulary. Use it when the operation is a computation or task
15//! whose success value matters. Use `Runnable<E>` when the operation only needs
16//! to report success or failure.
17//!
18//! The trait itself does not require `Send`; concurrent executors should add
19//! `+ Send + 'static` at their API boundary.
20//!
21//! # Author
22//!
23//! Haixing Hu
24
25use crate::{
26 functions::macros::impl_function_debug_display,
27 macros::{
28 impl_box_once_conversions,
29 impl_closure_once_trait,
30 impl_common_name_methods,
31 impl_common_new_methods,
32 },
33 suppliers::supplier_once::SupplierOnce,
34 tasks::runnable::BoxRunnable,
35};
36
37// ============================================================================
38// Callable Trait
39// ============================================================================
40
41/// A fallible one-time computation.
42///
43/// `Callable<R, E>` consumes itself and returns `Result<R, E>`. It is a
44/// semantic specialization of `SupplierOnce<Result<R, E>>` for executable
45/// computations and deferred tasks.
46///
47/// # Type Parameters
48///
49/// * `R` - The success value returned by the computation.
50/// * `E` - The error value returned when the computation fails.
51///
52/// # Examples
53///
54/// ```rust
55/// use qubit_function::Callable;
56///
57/// let task = || Ok::<i32, String>(21 * 2);
58/// assert_eq!(task.call(), Ok(42));
59/// ```
60///
61/// # Author
62///
63/// Haixing Hu
64pub trait Callable<R, E> {
65 /// Executes the computation, consuming `self`.
66 ///
67 /// # Returns
68 ///
69 /// Returns `Ok(R)` when the computation succeeds, or `Err(E)` when it
70 /// fails. The exact error meaning is defined by the concrete callable.
71 fn call(self) -> Result<R, E>;
72
73 /// Converts this callable into a boxed callable.
74 ///
75 /// # Returns
76 ///
77 /// A `BoxCallable<R, E>` that executes this callable when `call()` is
78 /// invoked.
79 fn into_box(self) -> BoxCallable<R, E>
80 where
81 Self: Sized + 'static,
82 {
83 BoxCallable::new(move || self.call())
84 }
85
86 /// Converts this callable into a closure.
87 ///
88 /// # Returns
89 ///
90 /// A closure implementing `FnOnce() -> Result<R, E>`.
91 fn into_fn(self) -> impl FnOnce() -> Result<R, E>
92 where
93 Self: Sized + 'static,
94 {
95 move || self.call()
96 }
97
98 /// Converts this callable into a boxed callable without consuming `self`.
99 ///
100 /// The method clones `self` and boxes the clone. Use this for cloneable
101 /// callable values that need to be reused after boxing.
102 ///
103 /// # Returns
104 ///
105 /// A new `BoxCallable<R, E>` built from a clone of this callable.
106 fn to_box(&self) -> BoxCallable<R, E>
107 where
108 Self: Clone + Sized + 'static,
109 {
110 self.clone().into_box()
111 }
112
113 /// Converts this callable into a closure without consuming `self`.
114 ///
115 /// The method clones `self` and returns a one-time closure that executes
116 /// the clone.
117 ///
118 /// # Returns
119 ///
120 /// A closure implementing `FnOnce() -> Result<R, E>`.
121 fn to_fn(&self) -> impl FnOnce() -> Result<R, E>
122 where
123 Self: Clone + Sized + 'static,
124 {
125 self.clone().into_fn()
126 }
127
128 /// Converts this callable into a runnable by discarding the success value.
129 ///
130 /// The returned runnable preserves errors and maps any `Ok(R)` to
131 /// `Ok(())`.
132 ///
133 /// # Returns
134 ///
135 /// A `BoxRunnable<E>` that executes this callable and discards its success
136 /// value.
137 fn into_runnable(self) -> BoxRunnable<E>
138 where
139 Self: Sized + 'static,
140 {
141 BoxRunnable::new(move || self.call().map(|_| ()))
142 }
143}
144
145// ============================================================================
146// BoxCallable
147// ============================================================================
148
149/// Box-based one-time callable.
150///
151/// `BoxCallable<R, E>` stores a `Box<dyn FnOnce() -> Result<R, E>>` and can be
152/// executed only once. It is the boxed concrete implementation of
153/// [`Callable`].
154///
155/// # Type Parameters
156///
157/// * `R` - The success value returned by the computation.
158/// * `E` - The error value returned when the computation fails.
159///
160/// # Examples
161///
162/// ```rust
163/// use qubit_function::{BoxCallable, Callable};
164///
165/// let task = BoxCallable::new(|| Ok::<i32, String>(42));
166/// assert_eq!(task.call(), Ok(42));
167/// ```
168///
169/// # Author
170///
171/// Haixing Hu
172pub struct BoxCallable<R, E> {
173 /// The one-time closure executed by this callable.
174 function: Box<dyn FnOnce() -> Result<R, E>>,
175 /// The optional name of this callable.
176 name: Option<String>,
177}
178
179impl<R, E> BoxCallable<R, E> {
180 impl_common_new_methods!(
181 (FnOnce() -> Result<R, E> + 'static),
182 |function| Box::new(function),
183 "callable"
184 );
185
186 /// Creates a boxed callable from a one-time supplier.
187 ///
188 /// This is an explicit bridge from `SupplierOnce<Result<R, E>>` to
189 /// `Callable<R, E>`.
190 ///
191 /// # Parameters
192 ///
193 /// * `supplier` - The supplier that produces the callable result.
194 ///
195 /// # Returns
196 ///
197 /// A new `BoxCallable<R, E>`.
198 #[inline]
199 pub fn from_supplier<S>(supplier: S) -> Self
200 where
201 S: SupplierOnce<Result<R, E>> + 'static,
202 {
203 Self::new(move || supplier.get())
204 }
205
206 impl_common_name_methods!("callable");
207
208 /// Maps the success value of this callable.
209 ///
210 /// # Parameters
211 ///
212 /// * `mapper` - Function that transforms the success value.
213 ///
214 /// # Returns
215 ///
216 /// A new callable that applies `mapper` when this callable succeeds.
217 #[inline]
218 pub fn map<U, M>(self, mapper: M) -> BoxCallable<U, E>
219 where
220 M: FnOnce(R) -> U + 'static,
221 R: 'static,
222 E: 'static,
223 {
224 let name = self.name;
225 let function = self.function;
226 BoxCallable::new_with_optional_name(move || function().map(mapper), name)
227 }
228
229 /// Maps the error value of this callable.
230 ///
231 /// # Parameters
232 ///
233 /// * `mapper` - Function that transforms the error value.
234 ///
235 /// # Returns
236 ///
237 /// A new callable that applies `mapper` when this callable fails.
238 #[inline]
239 pub fn map_err<E2, M>(self, mapper: M) -> BoxCallable<R, E2>
240 where
241 M: FnOnce(E) -> E2 + 'static,
242 R: 'static,
243 E: 'static,
244 {
245 let name = self.name;
246 let function = self.function;
247 BoxCallable::new_with_optional_name(move || function().map_err(mapper), name)
248 }
249
250 /// Chains another fallible computation after this callable succeeds.
251 ///
252 /// # Parameters
253 ///
254 /// * `next` - Function that receives the success value and returns the next
255 /// result.
256 ///
257 /// # Returns
258 ///
259 /// A new callable that runs `next` only when this callable succeeds.
260 #[inline]
261 pub fn and_then<U, N>(self, next: N) -> BoxCallable<U, E>
262 where
263 N: FnOnce(R) -> Result<U, E> + 'static,
264 R: 'static,
265 E: 'static,
266 {
267 let name = self.name;
268 let function = self.function;
269 BoxCallable::new_with_optional_name(move || function().and_then(next), name)
270 }
271}
272
273impl<R, E> Callable<R, E> for BoxCallable<R, E> {
274 /// Executes the boxed callable.
275 #[inline]
276 fn call(self) -> Result<R, E> {
277 (self.function)()
278 }
279
280 impl_box_once_conversions!(BoxCallable<R, E>, Callable, FnOnce() -> Result<R, E>);
281
282 /// Converts this boxed callable into a boxed runnable while preserving its
283 /// name.
284 #[inline]
285 fn into_runnable(self) -> BoxRunnable<E>
286 where
287 Self: Sized + 'static,
288 {
289 let name = self.name;
290 let function = self.function;
291 BoxRunnable::new_with_optional_name(move || function().map(|_| ()), name)
292 }
293}
294
295impl<R, E> SupplierOnce<Result<R, E>> for BoxCallable<R, E> {
296 /// Executes the boxed callable as a one-time supplier of `Result<R, E>`.
297 #[inline]
298 fn get(self) -> Result<R, E> {
299 self.call()
300 }
301}
302
303impl_closure_once_trait!(
304 Callable<R, E>,
305 call,
306 BoxCallable,
307 FnOnce() -> Result<R, E>
308);
309
310impl_function_debug_display!(BoxCallable<R, E>);