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 std::fmt;
26
27use crate::{
28 suppliers::supplier_once::SupplierOnce,
29 tasks::runnable::BoxRunnable,
30};
31
32// ============================================================================
33// Callable Trait
34// ============================================================================
35
36/// A fallible one-time computation.
37///
38/// `Callable<R, E>` consumes itself and returns `Result<R, E>`. It is a
39/// semantic specialization of `SupplierOnce<Result<R, E>>` for executable
40/// computations and deferred tasks.
41///
42/// # Type Parameters
43///
44/// * `R` - The success value returned by the computation.
45/// * `E` - The error value returned when the computation fails.
46///
47/// # Examples
48///
49/// ```rust
50/// use qubit_function::Callable;
51///
52/// let task = || Ok::<i32, String>(21 * 2);
53/// assert_eq!(task.call(), Ok(42));
54/// ```
55///
56/// # Author
57///
58/// Haixing Hu
59pub trait Callable<R, E> {
60 /// Executes the computation, consuming `self`.
61 ///
62 /// # Returns
63 ///
64 /// Returns `Ok(R)` when the computation succeeds, or `Err(E)` when it
65 /// fails. The exact error meaning is defined by the concrete callable.
66 fn call(self) -> Result<R, E>;
67
68 /// Converts this callable into a boxed callable.
69 ///
70 /// # Returns
71 ///
72 /// A `BoxCallable<R, E>` that executes this callable when `call()` is
73 /// invoked.
74 fn into_box(self) -> BoxCallable<R, E>
75 where
76 Self: Sized + 'static,
77 {
78 BoxCallable::new(move || self.call())
79 }
80
81 /// Converts this callable into a closure.
82 ///
83 /// # Returns
84 ///
85 /// A closure implementing `FnOnce() -> Result<R, E>`.
86 fn into_fn(self) -> impl FnOnce() -> Result<R, E>
87 where
88 Self: Sized + 'static,
89 {
90 move || self.call()
91 }
92
93 /// Converts this callable into a boxed callable without consuming `self`.
94 ///
95 /// The method clones `self` and boxes the clone. Use this for cloneable
96 /// callable values that need to be reused after boxing.
97 ///
98 /// # Returns
99 ///
100 /// A new `BoxCallable<R, E>` built from a clone of this callable.
101 fn to_box(&self) -> BoxCallable<R, E>
102 where
103 Self: Clone + Sized + 'static,
104 {
105 self.clone().into_box()
106 }
107
108 /// Converts this callable into a closure without consuming `self`.
109 ///
110 /// The method clones `self` and returns a one-time closure that executes
111 /// the clone.
112 ///
113 /// # Returns
114 ///
115 /// A closure implementing `FnOnce() -> Result<R, E>`.
116 fn to_fn(&self) -> impl FnOnce() -> Result<R, E>
117 where
118 Self: Clone + Sized + 'static,
119 {
120 self.clone().into_fn()
121 }
122
123 /// Converts this callable into a runnable by discarding the success value.
124 ///
125 /// The returned runnable preserves errors and maps any `Ok(R)` to
126 /// `Ok(())`.
127 ///
128 /// # Returns
129 ///
130 /// A `BoxRunnable<E>` that executes this callable and discards its success
131 /// value.
132 fn into_runnable(self) -> BoxRunnable<E>
133 where
134 Self: Sized + 'static,
135 {
136 BoxRunnable::new(move || self.call().map(|_| ()))
137 }
138}
139
140// ============================================================================
141// BoxCallable
142// ============================================================================
143
144/// Box-based one-time callable.
145///
146/// `BoxCallable<R, E>` stores a `Box<dyn FnOnce() -> Result<R, E>>` and can be
147/// executed only once. It is the boxed concrete implementation of
148/// [`Callable`].
149///
150/// # Type Parameters
151///
152/// * `R` - The success value returned by the computation.
153/// * `E` - The error value returned when the computation fails.
154///
155/// # Examples
156///
157/// ```rust
158/// use qubit_function::{BoxCallable, Callable};
159///
160/// let task = BoxCallable::new(|| Ok::<i32, String>(42));
161/// assert_eq!(task.call(), Ok(42));
162/// ```
163///
164/// # Author
165///
166/// Haixing Hu
167pub struct BoxCallable<R, E> {
168 /// The one-time closure executed by this callable.
169 function: Box<dyn FnOnce() -> Result<R, E>>,
170 /// The optional name of this callable.
171 name: Option<String>,
172}
173
174impl<R, E> BoxCallable<R, E> {
175 /// Creates a new boxed callable.
176 ///
177 /// # Parameters
178 ///
179 /// * `function` - The one-time closure executed by this callable.
180 ///
181 /// # Returns
182 ///
183 /// A new unnamed `BoxCallable<R, E>`.
184 #[inline]
185 pub fn new<F>(function: F) -> Self
186 where
187 F: FnOnce() -> Result<R, E> + 'static,
188 {
189 Self {
190 function: Box::new(function),
191 name: None,
192 }
193 }
194
195 /// Creates a new named boxed callable.
196 ///
197 /// # Parameters
198 ///
199 /// * `name` - Name used by `Debug` and `Display`.
200 /// * `function` - The one-time closure executed by this callable.
201 ///
202 /// # Returns
203 ///
204 /// A new named `BoxCallable<R, E>`.
205 #[inline]
206 pub fn new_with_name<F>(name: &str, function: F) -> Self
207 where
208 F: FnOnce() -> Result<R, E> + 'static,
209 {
210 Self {
211 function: Box::new(function),
212 name: Some(name.to_string()),
213 }
214 }
215
216 /// Creates a new boxed callable with an optional name.
217 ///
218 /// # Parameters
219 ///
220 /// * `function` - The one-time closure executed by this callable.
221 /// * `name` - Optional name used by `Debug` and `Display`.
222 ///
223 /// # Returns
224 ///
225 /// A new `BoxCallable<R, E>`.
226 #[inline]
227 pub fn new_with_optional_name<F>(function: F, name: Option<String>) -> Self
228 where
229 F: FnOnce() -> Result<R, E> + 'static,
230 {
231 Self {
232 function: Box::new(function),
233 name,
234 }
235 }
236
237 /// Creates a boxed callable from a one-time supplier.
238 ///
239 /// This is an explicit bridge from `SupplierOnce<Result<R, E>>` to
240 /// `Callable<R, E>`.
241 ///
242 /// # Parameters
243 ///
244 /// * `supplier` - The supplier that produces the callable result.
245 ///
246 /// # Returns
247 ///
248 /// A new `BoxCallable<R, E>`.
249 #[inline]
250 pub fn from_supplier<S>(supplier: S) -> Self
251 where
252 S: SupplierOnce<Result<R, E>> + 'static,
253 {
254 Self::new(move || supplier.get())
255 }
256
257 /// Gets the optional callable name.
258 ///
259 /// # Returns
260 ///
261 /// Returns `Some(&str)` if a name was set, or `None` otherwise.
262 #[inline]
263 pub fn name(&self) -> Option<&str> {
264 self.name.as_deref()
265 }
266
267 /// Sets the callable name.
268 ///
269 /// # Parameters
270 ///
271 /// * `name` - The new name.
272 #[inline]
273 pub fn set_name(&mut self, name: &str) {
274 if self.name.as_deref() != Some(name) {
275 self.name = Some(name.to_owned());
276 }
277 }
278
279 /// Clears the callable name.
280 #[inline]
281 pub fn clear_name(&mut self) {
282 self.name = None;
283 }
284
285 /// Maps the success value of this callable.
286 ///
287 /// # Parameters
288 ///
289 /// * `mapper` - Function that transforms the success value.
290 ///
291 /// # Returns
292 ///
293 /// A new callable that applies `mapper` when this callable succeeds.
294 #[inline]
295 pub fn map<U, M>(self, mapper: M) -> BoxCallable<U, E>
296 where
297 M: FnOnce(R) -> U + 'static,
298 R: 'static,
299 E: 'static,
300 {
301 let name = self.name;
302 let function = self.function;
303 BoxCallable::new_with_optional_name(move || function().map(mapper), name)
304 }
305
306 /// Maps the error value of this callable.
307 ///
308 /// # Parameters
309 ///
310 /// * `mapper` - Function that transforms the error value.
311 ///
312 /// # Returns
313 ///
314 /// A new callable that applies `mapper` when this callable fails.
315 #[inline]
316 pub fn map_err<E2, M>(self, mapper: M) -> BoxCallable<R, E2>
317 where
318 M: FnOnce(E) -> E2 + 'static,
319 R: 'static,
320 E: 'static,
321 {
322 let name = self.name;
323 let function = self.function;
324 BoxCallable::new_with_optional_name(move || function().map_err(mapper), name)
325 }
326
327 /// Chains another fallible computation after this callable succeeds.
328 ///
329 /// # Parameters
330 ///
331 /// * `next` - Function that receives the success value and returns the next
332 /// result.
333 ///
334 /// # Returns
335 ///
336 /// A new callable that runs `next` only when this callable succeeds.
337 #[inline]
338 pub fn and_then<U, N>(self, next: N) -> BoxCallable<U, E>
339 where
340 N: FnOnce(R) -> Result<U, E> + 'static,
341 R: 'static,
342 E: 'static,
343 {
344 let name = self.name;
345 let function = self.function;
346 BoxCallable::new_with_optional_name(move || function().and_then(next), name)
347 }
348}
349
350impl<R, E> Callable<R, E> for BoxCallable<R, E> {
351 /// Executes the boxed callable.
352 #[inline]
353 fn call(self) -> Result<R, E> {
354 (self.function)()
355 }
356
357 /// Returns this boxed callable without re-boxing it.
358 #[inline]
359 fn into_box(self) -> BoxCallable<R, E> {
360 self
361 }
362
363 /// Extracts the underlying one-time closure.
364 #[inline]
365 fn into_fn(self) -> impl FnOnce() -> Result<R, E> {
366 self.function
367 }
368
369 /// Converts this boxed callable into a boxed runnable while preserving its
370 /// name.
371 #[inline]
372 fn into_runnable(self) -> BoxRunnable<E>
373 where
374 Self: Sized + 'static,
375 {
376 let name = self.name;
377 let function = self.function;
378 BoxRunnable::new_with_optional_name(move || function().map(|_| ()), name)
379 }
380}
381
382impl<R, E> SupplierOnce<Result<R, E>> for BoxCallable<R, E> {
383 /// Executes the boxed callable as a one-time supplier of `Result<R, E>`.
384 #[inline]
385 fn get(self) -> Result<R, E> {
386 self.call()
387 }
388}
389
390impl<F, R, E> Callable<R, E> for F
391where
392 F: FnOnce() -> Result<R, E>,
393{
394 /// Executes the closure as a callable.
395 #[inline]
396 fn call(self) -> Result<R, E> {
397 self()
398 }
399
400 /// Converts the closure to a boxed callable.
401 #[inline]
402 fn into_box(self) -> BoxCallable<R, E>
403 where
404 Self: Sized + 'static,
405 {
406 BoxCallable::new(self)
407 }
408
409 /// Returns the closure unchanged.
410 #[inline]
411 fn into_fn(self) -> impl FnOnce() -> Result<R, E>
412 where
413 Self: Sized + 'static,
414 {
415 self
416 }
417}
418
419impl<R, E> fmt::Debug for BoxCallable<R, E> {
420 /// Formats this boxed callable for debugging.
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422 f.debug_struct("BoxCallable")
423 .field("name", &self.name)
424 .field("function", &"<function>")
425 .finish()
426 }
427}
428
429impl<R, E> fmt::Display for BoxCallable<R, E> {
430 /// Formats this boxed callable for display.
431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 match &self.name {
433 Some(name) => write!(f, "BoxCallable({name})"),
434 None => write!(f, "BoxCallable"),
435 }
436 }
437}