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