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