Skip to main content

qubit_function/tasks/
callable_once.rs

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