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