Skip to main content

qubit_function/tasks/runnable/
box_runnable.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the `BoxRunnable` public type.
10
11#![allow(unused_imports)]
12
13use super::*;
14
15// ============================================================================
16// BoxRunnable
17// ============================================================================
18
19/// Box-based reusable runnable.
20///
21/// `BoxRunnable<E>` stores a `Box<dyn FnMut() -> Result<(), E>>` and can be
22/// executed repeatedly. It is the boxed concrete implementation of
23/// [`Runnable`].
24///
25/// # Type Parameters
26///
27/// * `E` - The error value returned when the action fails.
28///
29/// # Examples
30///
31/// ```rust
32/// use qubit_function::{BoxRunnable, Runnable};
33///
34/// let mut task = BoxRunnable::new(|| Ok::<(), String>(()));
35/// assert_eq!(task.run(), Ok(()));
36/// ```
37///
38/// # Author
39///
40/// Haixing Hu
41pub struct BoxRunnable<E> {
42    /// The stateful closure executed by this runnable.
43    pub(super) function: Box<dyn FnMut() -> Result<(), E>>,
44    /// The optional name of this runnable.
45    pub(super) name: Option<String>,
46}
47
48impl<E> BoxRunnable<E> {
49    impl_common_new_methods!(
50        (FnMut() -> Result<(), E> + 'static),
51        |function| Box::new(function),
52        "runnable"
53    );
54
55    /// Creates a boxed runnable from a reusable supplier.
56    ///
57    /// This is an explicit bridge from `Supplier<Result<(), E>>` to
58    /// `Runnable<E>`.
59    ///
60    /// # Parameters
61    ///
62    /// * `supplier` - The supplier that produces the runnable result.
63    ///
64    /// # Returns
65    ///
66    /// A new `BoxRunnable<E>`.
67    #[inline]
68    pub fn from_supplier<S>(supplier: S) -> Self
69    where
70        S: Supplier<Result<(), E>> + '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 runnable executing both actions in sequence.
88    #[inline]
89    pub fn and_then<N>(self, next: N) -> BoxRunnable<E>
90    where
91        N: Runnable<E> + 'static,
92        E: 'static,
93    {
94        let name = self.name;
95        let mut function = self.function;
96        let mut next = next;
97        BoxRunnable::new_with_optional_name(
98            move || {
99                function()?;
100                next.run()
101            },
102            name,
103        )
104    }
105
106    /// Runs this runnable before a callable.
107    ///
108    /// The callable is not executed if this runnable returns `Err`.
109    ///
110    /// # Parameters
111    ///
112    /// * `callable` - The callable to execute after this runnable succeeds.
113    ///
114    /// # Returns
115    ///
116    /// A callable producing the second computation's result.
117    #[inline]
118    pub fn then_callable<R, C>(self, callable: C) -> BoxCallable<R, E>
119    where
120        C: crate::tasks::callable::Callable<R, E> + 'static,
121        R: 'static,
122        E: 'static,
123    {
124        let name = self.name;
125        let mut function = self.function;
126        let mut callable = callable;
127        BoxCallable::new_with_optional_name(
128            move || {
129                function()?;
130                callable.call()
131            },
132            name,
133        )
134    }
135}
136
137impl<E> Runnable<E> for BoxRunnable<E> {
138    /// Executes the boxed runnable.
139    #[inline]
140    fn run(&mut self) -> Result<(), E> {
141        (self.function)()
142    }
143
144    impl_box_conversions!(
145        BoxRunnable<E>,
146        RcRunnable,
147        FnMut() -> Result<(), E>
148    );
149
150    /// Converts this boxed runnable into a boxed callable while preserving its
151    /// name.
152    #[inline]
153    fn into_callable(self) -> BoxCallable<(), E>
154    where
155        Self: Sized + 'static,
156    {
157        let name = self.name;
158        let mut function = self.function;
159        BoxCallable::new_with_optional_name(
160            move || {
161                function()?;
162                Ok(())
163            },
164            name,
165        )
166    }
167}