Skip to main content

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