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