Skip to main content

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}