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>);