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
45mod box_runnable;
46pub use box_runnable::BoxRunnable;
47mod rc_runnable;
48pub use rc_runnable::RcRunnable;
49mod arc_runnable;
50pub use arc_runnable::ArcRunnable;
51
52// ============================================================================
53// Runnable Trait
54// ============================================================================
55
56/// A fallible, reusable, zero-argument action.
57///
58/// Conceptually, `Runnable<E>` matches [`FnMut`] `() -> Result<(), E>`, but
59/// uses task-oriented vocabulary. Prefer it when the operation's side effect
60/// matters and only success or failure need to be reported.
61///
62/// Each call borrows `self` mutably and returns [`Result::Ok`] with unit or
63/// [`Result::Err`] with `E`. Semantically, this is a specialization of
64/// [`SupplierOnce`]`<Result<(), E>>` for executable actions and deferred side
65/// effects.
66///
67/// The trait does not require [`Send`]. Concurrent executors should require
68/// `Runnable<E> + Send + 'static` (or similar) at their API boundary.
69///
70/// # Type Parameters
71///
72/// * `E` - The error value returned when the action fails.
73///
74/// # Examples
75///
76/// ```rust
77/// use qubit_function::Runnable;
78///
79/// let mut task = || Ok::<(), String>(());
80/// assert_eq!(task.run(), Ok(()));
81/// ```
82///
83/// # Author
84///
85/// Haixing Hu
86pub trait Runnable<E> {
87 /// Executes the action, borrowing `self` mutably.
88 ///
89 /// # Returns
90 ///
91 /// Returns `Ok(())` when the action succeeds, or `Err(E)` when it fails.
92 /// The exact error meaning is defined by the concrete runnable.
93 fn run(&mut self) -> Result<(), E>;
94
95 /// Converts this runnable into a boxed runnable.
96 ///
97 /// # Returns
98 ///
99 /// A `BoxRunnable<E>` that executes this runnable when `run()` is invoked.
100 fn into_box(mut self) -> BoxRunnable<E>
101 where
102 Self: Sized + 'static,
103 {
104 BoxRunnable::new(move || self.run())
105 }
106
107 /// Converts this runnable into a shared single-threaded runnable.
108 ///
109 /// # Returns
110 ///
111 /// An `RcRunnable<E>` that executes this runnable when `run()` is invoked.
112 fn into_rc(mut self) -> RcRunnable<E>
113 where
114 Self: Sized + 'static,
115 {
116 RcRunnable::new(move || self.run())
117 }
118
119 /// Converts this runnable into a shared thread-safe runnable.
120 ///
121 /// # Returns
122 ///
123 /// An `ArcRunnable<E>` that executes this runnable when `run()` is invoked.
124 fn into_arc(mut self) -> ArcRunnable<E>
125 where
126 Self: Sized + Send + 'static,
127 {
128 ArcRunnable::new(move || self.run())
129 }
130
131 /// Converts this runnable into a mutable closure.
132 ///
133 /// # Returns
134 ///
135 /// A closure implementing `FnMut() -> Result<(), E>`.
136 fn into_fn(mut self) -> impl FnMut() -> Result<(), E>
137 where
138 Self: Sized + 'static,
139 {
140 move || self.run()
141 }
142
143 /// Converts this runnable into a boxed runnable without consuming `self`.
144 ///
145 /// The method clones `self` and boxes the clone. Use this for cloneable
146 /// runnable values that need to be reused after boxing.
147 ///
148 /// # Returns
149 ///
150 /// A new `BoxRunnable<E>` built from a clone of this runnable.
151 fn to_box(&self) -> BoxRunnable<E>
152 where
153 Self: Clone + Sized + 'static,
154 {
155 self.clone().into_box()
156 }
157
158 /// Converts this runnable into a mutable closure without consuming `self`.
159 ///
160 /// The method clones `self` and returns a mutable closure that executes
161 /// the clone.
162 ///
163 /// # Returns
164 ///
165 /// A closure implementing `FnMut() -> Result<(), E>`.
166 fn to_fn(&self) -> impl FnMut() -> Result<(), E>
167 where
168 Self: Clone + Sized + 'static,
169 {
170 self.clone().into_fn()
171 }
172
173 /// Converts this runnable into a shared single-threaded runnable without
174 /// consuming `self`.
175 ///
176 /// The method clones `self` and wraps the clone.
177 ///
178 /// # Returns
179 ///
180 /// A new `RcRunnable<E>` built from a clone of this runnable.
181 fn to_rc(&self) -> RcRunnable<E>
182 where
183 Self: Clone + Sized + 'static,
184 {
185 self.clone().into_rc()
186 }
187
188 /// Converts this runnable into a shared thread-safe runnable without
189 /// consuming `self`.
190 ///
191 /// The method clones `self` and wraps the clone.
192 ///
193 /// # Returns
194 ///
195 /// A new `ArcRunnable<E>` built from a clone of this runnable.
196 fn to_arc(&self) -> ArcRunnable<E>
197 where
198 Self: Clone + Send + Sized + 'static,
199 {
200 self.clone().into_arc()
201 }
202
203 /// Converts this runnable into a callable returning unit.
204 ///
205 /// # Returns
206 ///
207 /// A `BoxCallable<(), E>` that executes this runnable and returns
208 /// `Ok(())` on success.
209 fn into_callable(self) -> BoxCallable<(), E>
210 where
211 Self: Sized + 'static,
212 {
213 let mut runnable = self;
214 BoxCallable::new(move || runnable.run())
215 }
216}