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