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