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}