Skip to main content

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