Skip to main content

qubit_function/tasks/callable_once/
box_callable_once.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10// qubit-style: allow explicit-imports
11//! Defines the `BoxCallableOnce` public type.
12
13use crate::{
14    functions::macros::impl_function_debug_display,
15    macros::{
16        impl_box_once_conversions,
17        impl_common_name_methods,
18        impl_common_new_methods,
19    },
20    suppliers::supplier_once::SupplierOnce,
21    tasks::{
22        callable_once::{
23            CallableOnce,
24            LocalBoxCallableOnce,
25        },
26        runnable_once::{
27            BoxRunnableOnce,
28            LocalBoxRunnableOnce,
29        },
30    },
31};
32
33// ============================================================================
34// BoxCallableOnce
35// ============================================================================
36
37/// Box-based one-time callable.
38///
39/// `BoxCallableOnce<R, E>` stores a
40/// `Box<dyn FnOnce() -> Result<R, E> + Send>` and can be executed only once.
41/// It is the boxed concrete implementation of [`CallableOnce`] for task
42/// objects that may be moved across threads.
43///
44/// # Type Parameters
45///
46/// * `R` - The success value returned by the computation.
47/// * `E` - The error value returned when the computation fails.
48///
49/// # Examples
50///
51/// ```rust
52/// use qubit_function::{BoxCallableOnce, CallableOnce};
53///
54/// let task = BoxCallableOnce::new(|| Ok::<i32, String>(42));
55/// assert_eq!(task.call(), Ok(42));
56/// ```
57///
58pub struct BoxCallableOnce<R, E> {
59    /// The one-time closure executed by this callable.
60    pub(super) function: Box<dyn FnOnce() -> Result<R, E> + Send>,
61    /// The optional name of this callable.
62    pub(super) name: Option<String>,
63}
64
65impl<R, E> BoxCallableOnce<R, E> {
66    impl_common_new_methods!(
67        (FnOnce() -> Result<R, E> + Send + 'static),
68        |function| Box::new(function),
69        "callable"
70    );
71
72    /// Creates a boxed callable from a one-time supplier.
73    ///
74    /// This is an explicit bridge from `SupplierOnce<Result<R, E>>` to
75    /// `CallableOnce<R, E>`.
76    ///
77    /// # Parameters
78    ///
79    /// * `supplier` - The supplier that produces the callable result.
80    ///
81    /// # Returns
82    ///
83    /// A new `BoxCallableOnce<R, E>`.
84    #[inline]
85    pub fn from_supplier<S>(supplier: S) -> Self
86    where
87        S: SupplierOnce<Result<R, E>> + Send + 'static,
88    {
89        Self::new(move || supplier.get())
90    }
91
92    impl_common_name_methods!("callable");
93
94    /// Maps the success value of this callable.
95    ///
96    /// # Parameters
97    ///
98    /// * `mapper` - Function that transforms the success value.
99    ///
100    /// # Returns
101    ///
102    /// A new callable that applies `mapper` when this callable succeeds.
103    #[inline]
104    pub fn map<U, M>(self, mapper: M) -> BoxCallableOnce<U, E>
105    where
106        M: FnOnce(R) -> U + Send + 'static,
107        R: 'static,
108        E: 'static,
109    {
110        let name = self.name;
111        let function = self.function;
112        BoxCallableOnce::new_with_optional_name(move || function().map(mapper), name)
113    }
114
115    /// Maps the error value of this callable.
116    ///
117    /// # Parameters
118    ///
119    /// * `mapper` - Function that transforms the error value.
120    ///
121    /// # Returns
122    ///
123    /// A new callable that applies `mapper` when this callable fails.
124    #[inline]
125    pub fn map_err<E2, M>(self, mapper: M) -> BoxCallableOnce<R, E2>
126    where
127        M: FnOnce(E) -> E2 + Send + 'static,
128        R: 'static,
129        E: 'static,
130    {
131        let name = self.name;
132        let function = self.function;
133        BoxCallableOnce::new_with_optional_name(move || function().map_err(mapper), name)
134    }
135
136    /// Chains another fallible computation after this callable succeeds.
137    ///
138    /// # Parameters
139    ///
140    /// * `next` - Function that receives the success value and returns the next
141    ///   result.
142    ///
143    /// # Returns
144    ///
145    /// A new callable that runs `next` only when this callable succeeds.
146    #[inline]
147    pub fn and_then<U, N>(self, next: N) -> BoxCallableOnce<U, E>
148    where
149        N: FnOnce(R) -> Result<U, E> + Send + 'static,
150        R: 'static,
151        E: 'static,
152    {
153        let name = self.name;
154        let function = self.function;
155        BoxCallableOnce::new_with_optional_name(move || function().and_then(next), name)
156    }
157}
158
159impl<R, E> CallableOnce<R, E> for BoxCallableOnce<R, E> {
160    /// Executes the boxed callable.
161    #[inline]
162    fn call(self) -> Result<R, E> {
163        (self.function)()
164    }
165
166    impl_box_once_conversions!(BoxCallableOnce<R, E>, CallableOnce, FnOnce() -> Result<R, E>);
167
168    /// Converts this boxed callable into a boxed runnable while preserving its
169    /// name.
170    #[inline]
171    fn into_runnable(self) -> BoxRunnableOnce<E>
172    where
173        Self: Sized + 'static,
174    {
175        let name = self.name;
176        let function = self.function;
177        BoxRunnableOnce::new_with_optional_name(move || function().map(|_| ()), name)
178    }
179
180    /// Converts this boxed callable into a local boxed callable while
181    /// preserving its name.
182    #[inline]
183    fn into_local_box(self) -> LocalBoxCallableOnce<R, E>
184    where
185        Self: Sized + 'static,
186    {
187        let name = self.name;
188        let function = self.function;
189        LocalBoxCallableOnce::new_with_optional_name(function, name)
190    }
191
192    /// Converts this boxed callable into a local boxed runnable while
193    /// preserving its name.
194    #[inline]
195    fn into_local_runnable(self) -> LocalBoxRunnableOnce<E>
196    where
197        Self: Sized + 'static,
198    {
199        let name = self.name;
200        let function = self.function;
201        LocalBoxRunnableOnce::new_with_optional_name(move || function().map(|_| ()), name)
202    }
203}
204
205impl<R, E> SupplierOnce<Result<R, E>> for BoxCallableOnce<R, E> {
206    /// Executes the boxed callable as a one-time supplier of `Result<R, E>`.
207    #[inline]
208    fn get(self) -> Result<R, E> {
209        self.call()
210    }
211}
212
213impl<F, R, E> CallableOnce<R, E> for F
214where
215    F: FnOnce() -> Result<R, E>,
216{
217    /// Executes the closure as a one-time callable.
218    #[inline]
219    fn call(self) -> Result<R, E> {
220        self()
221    }
222}
223
224impl_function_debug_display!(BoxCallableOnce<R, E>);