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