Skip to main content

qubit_function/tasks/runnable/
box_runnable.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 `BoxRunnable` public type.
12
13#![allow(unused_imports)]
14
15use super::*;
16
17// ============================================================================
18// BoxRunnable
19// ============================================================================
20
21/// Box-based reusable runnable.
22///
23/// `BoxRunnable<E>` stores a `Box<dyn FnMut() -> Result<(), E>>` and can be
24/// executed repeatedly. It is the boxed concrete implementation of
25/// [`Runnable`].
26///
27/// # Type Parameters
28///
29/// * `E` - The error value returned when the action fails.
30///
31/// # Examples
32///
33/// ```rust
34/// use qubit_function::{BoxRunnable, Runnable};
35///
36/// let mut task = BoxRunnable::new(|| Ok::<(), String>(()));
37/// assert_eq!(task.run(), Ok(()));
38/// ```
39///
40pub struct BoxRunnable<E> {
41    /// The stateful closure executed by this runnable.
42    pub(super) function: Box<dyn FnMut() -> Result<(), E>>,
43    /// The optional name of this runnable.
44    pub(super) name: Option<String>,
45}
46
47impl<E> BoxRunnable<E> {
48    impl_common_new_methods!(
49        (FnMut() -> Result<(), E> + 'static),
50        |function| Box::new(function),
51        "runnable"
52    );
53
54    /// Creates a boxed runnable from a reusable supplier.
55    ///
56    /// This is an explicit bridge from `Supplier<Result<(), E>>` to
57    /// `Runnable<E>`.
58    ///
59    /// # Parameters
60    ///
61    /// * `supplier` - The supplier that produces the runnable result.
62    ///
63    /// # Returns
64    ///
65    /// A new `BoxRunnable<E>`.
66    #[inline]
67    pub fn from_supplier<S>(supplier: S) -> Self
68    where
69        S: Supplier<Result<(), E>> + 'static,
70    {
71        Self::new(move || supplier.get())
72    }
73
74    impl_common_name_methods!("runnable");
75
76    /// Chains another runnable after this runnable succeeds.
77    ///
78    /// The second runnable is not executed if this runnable returns `Err`.
79    ///
80    /// # Parameters
81    ///
82    /// * `next` - The runnable to execute after this runnable succeeds.
83    ///
84    /// # Returns
85    ///
86    /// A runnable executing both actions in sequence.
87    #[inline]
88    pub fn and_then<N>(self, next: N) -> BoxRunnable<E>
89    where
90        N: Runnable<E> + 'static,
91        E: 'static,
92    {
93        let name = self.name;
94        let mut function = self.function;
95        let mut next = next;
96        BoxRunnable::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) -> BoxCallable<R, E>
118    where
119        C: crate::tasks::callable::Callable<R, E> + 'static,
120        R: 'static,
121        E: 'static,
122    {
123        let name = self.name;
124        let mut function = self.function;
125        let mut callable = callable;
126        BoxCallable::new_with_optional_name(
127            move || {
128                function()?;
129                callable.call()
130            },
131            name,
132        )
133    }
134}
135
136impl<E> Runnable<E> for BoxRunnable<E> {
137    /// Executes the boxed runnable.
138    #[inline]
139    fn run(&mut self) -> Result<(), E> {
140        (self.function)()
141    }
142
143    impl_box_conversions!(
144        BoxRunnable<E>,
145        RcRunnable,
146        FnMut() -> Result<(), E>
147    );
148
149    /// Converts this boxed runnable into a boxed callable while preserving its
150    /// name.
151    #[inline]
152    fn into_callable(self) -> BoxCallable<(), E>
153    where
154        Self: Sized + 'static,
155    {
156        let name = self.name;
157        let mut function = self.function;
158        BoxCallable::new_with_optional_name(
159            move || {
160                function()?;
161                Ok(())
162            },
163            name,
164        )
165    }
166}