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 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>);