qubit_function/tasks/runnable.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # Runnable Types
10//!
11//! Provides fallible, reusable, zero-argument actions.
12//!
13//! A `Runnable<E>` is equivalent to `FnMut() -> Result<(), E>`, but uses
14//! task-oriented vocabulary. Use it when the operation's side effect matters
15//! and only success or failure should be reported.
16//!
17//! The trait itself does not require `Send`; concurrent executors should add
18//! `+ Send + 'static` at their API boundary.
19//!
20//! # Author
21//!
22//! Haixing Hu
23
24use std::cell::RefCell;
25use std::rc::Rc;
26use std::sync::Arc;
27
28use parking_lot::Mutex;
29
30use crate::{
31 macros::{
32 impl_arc_conversions,
33 impl_box_conversions,
34 impl_closure_trait,
35 impl_common_name_methods,
36 impl_common_new_methods,
37 impl_rc_conversions,
38 },
39 suppliers::macros::impl_supplier_debug_display,
40 suppliers::supplier::Supplier,
41 suppliers::supplier_once::SupplierOnce,
42 tasks::callable::BoxCallable,
43};
44
45// ============================================================================
46// Runnable Trait
47// ============================================================================
48
49/// A fallible, reusable, zero-argument action.
50///
51/// Conceptually, `Runnable<E>` matches [`FnMut`] `() -> Result<(), E>`, but
52/// uses task-oriented vocabulary. Prefer it when the operation's side effect
53/// matters and only success or failure need to be reported.
54///
55/// Each call borrows `self` mutably and returns [`Result::Ok`] with unit or
56/// [`Result::Err`] with `E`. Semantically, this is a specialization of
57/// [`SupplierOnce`]`<Result<(), E>>` for executable actions and deferred side
58/// effects.
59///
60/// The trait does not require [`Send`]. Concurrent executors should require
61/// `Runnable<E> + Send + 'static` (or similar) at their API boundary.
62///
63/// # Type Parameters
64///
65/// * `E` - The error value returned when the action fails.
66///
67/// # Examples
68///
69/// ```rust
70/// use qubit_function::Runnable;
71///
72/// let mut task = || Ok::<(), String>(());
73/// assert_eq!(task.run(), Ok(()));
74/// ```
75///
76/// # Author
77///
78/// Haixing Hu
79pub trait Runnable<E> {
80 /// Executes the action, borrowing `self` mutably.
81 ///
82 /// # Returns
83 ///
84 /// Returns `Ok(())` when the action succeeds, or `Err(E)` when it fails.
85 /// The exact error meaning is defined by the concrete runnable.
86 fn run(&mut self) -> Result<(), E>;
87
88 /// Converts this runnable into a boxed runnable.
89 ///
90 /// # Returns
91 ///
92 /// A `BoxRunnable<E>` that executes this runnable when `run()` is invoked.
93 fn into_box(mut self) -> BoxRunnable<E>
94 where
95 Self: Sized + 'static,
96 {
97 BoxRunnable::new(move || self.run())
98 }
99
100 /// Converts this runnable into a shared single-threaded runnable.
101 ///
102 /// # Returns
103 ///
104 /// An `RcRunnable<E>` that executes this runnable when `run()` is invoked.
105 fn into_rc(mut self) -> RcRunnable<E>
106 where
107 Self: Sized + 'static,
108 {
109 RcRunnable::new(move || self.run())
110 }
111
112 /// Converts this runnable into a shared thread-safe runnable.
113 ///
114 /// # Returns
115 ///
116 /// An `ArcRunnable<E>` that executes this runnable when `run()` is invoked.
117 fn into_arc(mut self) -> ArcRunnable<E>
118 where
119 Self: Sized + Send + 'static,
120 {
121 ArcRunnable::new(move || self.run())
122 }
123
124 /// Converts this runnable into a mutable closure.
125 ///
126 /// # Returns
127 ///
128 /// A closure implementing `FnMut() -> Result<(), E>`.
129 fn into_fn(mut self) -> impl FnMut() -> Result<(), E>
130 where
131 Self: Sized + 'static,
132 {
133 move || self.run()
134 }
135
136 /// Converts this runnable into a boxed runnable without consuming `self`.
137 ///
138 /// The method clones `self` and boxes the clone. Use this for cloneable
139 /// runnable values that need to be reused after boxing.
140 ///
141 /// # Returns
142 ///
143 /// A new `BoxRunnable<E>` built from a clone of this runnable.
144 fn to_box(&self) -> BoxRunnable<E>
145 where
146 Self: Clone + Sized + 'static,
147 {
148 self.clone().into_box()
149 }
150
151 /// Converts this runnable into a mutable closure without consuming `self`.
152 ///
153 /// The method clones `self` and returns a mutable closure that executes
154 /// the clone.
155 ///
156 /// # Returns
157 ///
158 /// A closure implementing `FnMut() -> Result<(), E>`.
159 fn to_fn(&self) -> impl FnMut() -> Result<(), E>
160 where
161 Self: Clone + Sized + 'static,
162 {
163 self.clone().into_fn()
164 }
165
166 /// Converts this runnable into a shared single-threaded runnable without
167 /// consuming `self`.
168 ///
169 /// The method clones `self` and wraps the clone.
170 ///
171 /// # Returns
172 ///
173 /// A new `RcRunnable<E>` built from a clone of this runnable.
174 fn to_rc(&self) -> RcRunnable<E>
175 where
176 Self: Clone + Sized + 'static,
177 {
178 self.clone().into_rc()
179 }
180
181 /// Converts this runnable into a shared thread-safe runnable without
182 /// consuming `self`.
183 ///
184 /// The method clones `self` and wraps the clone.
185 ///
186 /// # Returns
187 ///
188 /// A new `ArcRunnable<E>` built from a clone of this runnable.
189 fn to_arc(&self) -> ArcRunnable<E>
190 where
191 Self: Clone + Send + Sized + 'static,
192 {
193 self.clone().into_arc()
194 }
195
196 /// Converts this runnable into a callable returning unit.
197 ///
198 /// # Returns
199 ///
200 /// A `BoxCallable<(), E>` that executes this runnable and returns
201 /// `Ok(())` on success.
202 fn into_callable(self) -> BoxCallable<(), E>
203 where
204 Self: Sized + 'static,
205 {
206 let mut runnable = self;
207 BoxCallable::new(move || runnable.run())
208 }
209}
210
211// ============================================================================
212// BoxRunnable
213// ============================================================================
214
215/// Box-based reusable runnable.
216///
217/// `BoxRunnable<E>` stores a `Box<dyn FnMut() -> Result<(), E>>` and can be
218/// executed repeatedly. It is the boxed concrete implementation of
219/// [`Runnable`].
220///
221/// # Type Parameters
222///
223/// * `E` - The error value returned when the action fails.
224///
225/// # Examples
226///
227/// ```rust
228/// use qubit_function::{BoxRunnable, Runnable};
229///
230/// let mut task = BoxRunnable::new(|| Ok::<(), String>(()));
231/// assert_eq!(task.run(), Ok(()));
232/// ```
233///
234/// # Author
235///
236/// Haixing Hu
237pub struct BoxRunnable<E> {
238 /// The stateful closure executed by this runnable.
239 function: Box<dyn FnMut() -> Result<(), E>>,
240 /// The optional name of this runnable.
241 name: Option<String>,
242}
243
244impl<E> BoxRunnable<E> {
245 impl_common_new_methods!(
246 (FnMut() -> Result<(), E> + 'static),
247 |function| Box::new(function),
248 "runnable"
249 );
250
251 /// Creates a boxed runnable from a reusable supplier.
252 ///
253 /// This is an explicit bridge from `Supplier<Result<(), E>>` to
254 /// `Runnable<E>`.
255 ///
256 /// # Parameters
257 ///
258 /// * `supplier` - The supplier that produces the runnable result.
259 ///
260 /// # Returns
261 ///
262 /// A new `BoxRunnable<E>`.
263 #[inline]
264 pub fn from_supplier<S>(supplier: S) -> Self
265 where
266 S: Supplier<Result<(), E>> + 'static,
267 {
268 Self::new(move || supplier.get())
269 }
270
271 impl_common_name_methods!("runnable");
272
273 /// Chains another runnable after this runnable succeeds.
274 ///
275 /// The second runnable is not executed if this runnable returns `Err`.
276 ///
277 /// # Parameters
278 ///
279 /// * `next` - The runnable to execute after this runnable succeeds.
280 ///
281 /// # Returns
282 ///
283 /// A runnable executing both actions in sequence.
284 #[inline]
285 pub fn and_then<N>(self, next: N) -> BoxRunnable<E>
286 where
287 N: Runnable<E> + 'static,
288 E: 'static,
289 {
290 let name = self.name;
291 let mut function = self.function;
292 let mut next = next;
293 BoxRunnable::new_with_optional_name(
294 move || {
295 function()?;
296 next.run()
297 },
298 name,
299 )
300 }
301
302 /// Runs this runnable before a callable.
303 ///
304 /// The callable is not executed if this runnable returns `Err`.
305 ///
306 /// # Parameters
307 ///
308 /// * `callable` - The callable to execute after this runnable succeeds.
309 ///
310 /// # Returns
311 ///
312 /// A callable producing the second computation's result.
313 #[inline]
314 pub fn then_callable<R, C>(self, callable: C) -> BoxCallable<R, E>
315 where
316 C: crate::tasks::callable::Callable<R, E> + 'static,
317 R: 'static,
318 E: 'static,
319 {
320 let name = self.name;
321 let mut function = self.function;
322 let mut callable = callable;
323 BoxCallable::new_with_optional_name(
324 move || {
325 function()?;
326 callable.call()
327 },
328 name,
329 )
330 }
331}
332
333impl<E> Runnable<E> for BoxRunnable<E> {
334 /// Executes the boxed runnable.
335 #[inline]
336 fn run(&mut self) -> Result<(), E> {
337 (self.function)()
338 }
339
340 impl_box_conversions!(
341 BoxRunnable<E>,
342 RcRunnable,
343 FnMut() -> Result<(), E>
344 );
345
346 /// Converts this boxed runnable into a boxed callable while preserving its
347 /// name.
348 #[inline]
349 fn into_callable(self) -> BoxCallable<(), E>
350 where
351 Self: Sized + 'static,
352 {
353 let name = self.name;
354 let mut function = self.function;
355 BoxCallable::new_with_optional_name(
356 move || {
357 function()?;
358 Ok(())
359 },
360 name,
361 )
362 }
363}
364
365// ============================================================================
366// RcRunnable
367// ============================================================================
368
369/// Single-threaded shared runnable.
370///
371/// `RcRunnable<E>` stores a `Rc<RefCell<dyn FnMut() -> Result<(), E>>>` and can
372/// be called repeatedly through shared ownership.
373///
374/// # Type Parameters
375///
376/// * `E` - The error value returned when the action fails.
377///
378/// # Author
379///
380/// Haixing Hu
381pub struct RcRunnable<E> {
382 /// The stateful closure executed by this runnable.
383 function: Rc<RefCell<dyn FnMut() -> Result<(), E>>>,
384 /// The optional name of this runnable.
385 name: Option<String>,
386}
387
388impl<E> Clone for RcRunnable<E> {
389 #[inline]
390 fn clone(&self) -> Self {
391 Self {
392 function: Rc::clone(&self.function),
393 name: self.name.clone(),
394 }
395 }
396}
397
398impl<E> RcRunnable<E> {
399 impl_common_new_methods!(
400 (FnMut() -> Result<(), E> + 'static),
401 |function| Rc::new(RefCell::new(function)),
402 "runnable"
403 );
404
405 /// Creates a shared runnable from a reusable supplier.
406 ///
407 /// # Parameters
408 ///
409 /// * `supplier` - The supplier that produces the runnable result.
410 ///
411 /// # Returns
412 ///
413 /// A new `RcRunnable<E>`.
414 #[inline]
415 pub fn from_supplier<S>(supplier: S) -> Self
416 where
417 S: Supplier<Result<(), E>> + 'static,
418 {
419 Self::new(move || supplier.get())
420 }
421
422 impl_common_name_methods!("runnable");
423}
424
425impl<E> Runnable<E> for RcRunnable<E> {
426 /// Executes the shared runnable.
427 #[inline]
428 fn run(&mut self) -> Result<(), E> {
429 (self.function.borrow_mut())()
430 }
431
432 impl_rc_conversions!(
433 RcRunnable<E>,
434 BoxRunnable,
435 FnMut() -> Result<(), E>
436 );
437}
438
439// ============================================================================
440// ArcRunnable
441// ============================================================================
442
443/// Thread-safe runnable.
444///
445/// `ArcRunnable<E>` stores an `Arc<Mutex<dyn FnMut() -> Result<(), E> + Send>>`
446/// and can be called repeatedly across threads.
447///
448/// # Type Parameters
449///
450/// * `E` - The error value returned when the action fails.
451///
452/// # Author
453///
454/// Haixing Hu
455pub struct ArcRunnable<E> {
456 /// The stateful closure executed by this runnable.
457 function: Arc<Mutex<dyn FnMut() -> Result<(), E> + Send>>,
458 /// The optional name of this runnable.
459 name: Option<String>,
460}
461
462impl<E> Clone for ArcRunnable<E> {
463 #[inline]
464 fn clone(&self) -> Self {
465 Self {
466 function: Arc::clone(&self.function),
467 name: self.name.clone(),
468 }
469 }
470}
471
472impl<E> ArcRunnable<E> {
473 impl_common_new_methods!(
474 (FnMut() -> Result<(), E> + Send + 'static),
475 |function| Arc::new(Mutex::new(function)),
476 "runnable"
477 );
478
479 /// Creates a thread-safe runnable from a reusable supplier.
480 ///
481 /// # Parameters
482 ///
483 /// * `supplier` - The supplier that produces the runnable result.
484 ///
485 /// # Returns
486 ///
487 /// A new `ArcRunnable<E>`.
488 #[inline]
489 pub fn from_supplier<S>(supplier: S) -> Self
490 where
491 S: Supplier<Result<(), E>> + Send + 'static,
492 {
493 Self::new(move || supplier.get())
494 }
495
496 impl_common_name_methods!("runnable");
497}
498
499impl<E> Runnable<E> for ArcRunnable<E> {
500 /// Executes the thread-safe runnable.
501 #[inline]
502 fn run(&mut self) -> Result<(), E> {
503 (self.function.lock())()
504 }
505
506 impl_arc_conversions!(
507 ArcRunnable<E>,
508 BoxRunnable,
509 RcRunnable,
510 FnMut() -> Result<(), E>
511 );
512}
513
514impl<E> SupplierOnce<Result<(), E>> for BoxRunnable<E> {
515 /// Executes the boxed runnable as a one-time supplier of `Result<(), E>`.
516 #[inline]
517 fn get(mut self) -> Result<(), E> {
518 self.run()
519 }
520}
521
522impl_closure_trait!(
523 Runnable<E>,
524 run,
525 FnMut() -> Result<(), E>
526);
527
528impl_supplier_debug_display!(BoxRunnable<E>);