qubit_function/tasks/runnable_with.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # RunnableWith Types
10//!
11//! Provides fallible, reusable actions that operate on a mutable input.
12//!
13//! A `RunnableWith<T, E>` is equivalent to
14//! `FnMut(&mut T) -> Result<(), E>`, but uses task-oriented vocabulary. Use it
15//! when the operation needs access to protected or caller-provided state and
16//! 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//! # Author
22//!
23//! Haixing Hu
24
25use std::cell::RefCell;
26use std::rc::Rc;
27use std::sync::Arc;
28
29use parking_lot::Mutex;
30
31use crate::{
32 functions::macros::impl_function_debug_display,
33 macros::{
34 impl_arc_conversions,
35 impl_box_conversions,
36 impl_closure_trait,
37 impl_common_name_methods,
38 impl_common_new_methods,
39 impl_rc_conversions,
40 },
41 tasks::callable_with::BoxCallableWith,
42};
43
44/// A fallible, reusable action that receives mutable input.
45///
46/// Conceptually this is `FnMut(&mut T) -> Result<(), E>` with task-oriented
47/// naming. It is useful for executor-style APIs that run an action with access
48/// to protected state, such as a value held under a lock.
49///
50/// # Type Parameters
51///
52/// * `T` - The mutable input type.
53/// * `E` - The error value returned when the action fails.
54///
55/// # Author
56///
57/// Haixing Hu
58pub trait RunnableWith<T, E> {
59 /// Executes the action with mutable input.
60 ///
61 /// # Parameters
62 ///
63 /// * `input` - The mutable input passed to this task.
64 ///
65 /// # Returns
66 ///
67 /// Returns `Ok(())` when the action succeeds, or `Err(E)` when it fails.
68 /// The exact error meaning is defined by the concrete runnable.
69 fn run_with(&mut self, input: &mut T) -> Result<(), E>;
70
71 /// Converts this runnable into a boxed runnable.
72 ///
73 /// # Returns
74 ///
75 /// A `BoxRunnableWith<T, E>`.
76 fn into_box(mut self) -> BoxRunnableWith<T, E>
77 where
78 Self: Sized + 'static,
79 {
80 BoxRunnableWith::new(move |input| self.run_with(input))
81 }
82
83 /// Converts this runnable into an `Rc` runnable.
84 ///
85 /// # Returns
86 ///
87 /// A `RcRunnableWith<T, E>`.
88 fn into_rc(mut self) -> RcRunnableWith<T, E>
89 where
90 Self: Sized + 'static,
91 {
92 RcRunnableWith::new(move |input| self.run_with(input))
93 }
94
95 /// Converts this runnable into an `Arc` runnable.
96 ///
97 /// # Returns
98 ///
99 /// An `ArcRunnableWith<T, E>`.
100 fn into_arc(mut self) -> ArcRunnableWith<T, E>
101 where
102 Self: Sized + Send + 'static,
103 {
104 ArcRunnableWith::new(move |input| self.run_with(input))
105 }
106
107 /// Converts this runnable into a mutable closure.
108 ///
109 /// # Returns
110 ///
111 /// A closure implementing `FnMut(&mut T) -> Result<(), E>`.
112 fn into_fn(mut self) -> impl FnMut(&mut T) -> Result<(), E>
113 where
114 Self: Sized + 'static,
115 {
116 move |input| self.run_with(input)
117 }
118
119 /// Converts this runnable into a boxed runnable without consuming `self`.
120 ///
121 /// # Returns
122 ///
123 /// A `BoxRunnableWith<T, E>` built from a clone of this runnable.
124 fn to_box(&self) -> BoxRunnableWith<T, E>
125 where
126 Self: Clone + Sized + 'static,
127 {
128 self.clone().into_box()
129 }
130
131 /// Converts this runnable into an `Rc` runnable without consuming `self`.
132 ///
133 /// # Returns
134 ///
135 /// A `RcRunnableWith<T, E>` built from a clone of this runnable.
136 fn to_rc(&self) -> RcRunnableWith<T, E>
137 where
138 Self: Clone + Sized + 'static,
139 {
140 self.clone().into_rc()
141 }
142
143 /// Converts this runnable into an `Arc` runnable without consuming `self`.
144 ///
145 /// # Returns
146 ///
147 /// An `ArcRunnableWith<T, E>` built from a clone of this runnable.
148 fn to_arc(&self) -> ArcRunnableWith<T, E>
149 where
150 Self: Clone + Send + Sized + 'static,
151 {
152 self.clone().into_arc()
153 }
154
155 /// Converts this runnable into a mutable closure without consuming `self`.
156 ///
157 /// # Returns
158 ///
159 /// A closure implementing `FnMut(&mut T) -> Result<(), E>`.
160 fn to_fn(&self) -> impl FnMut(&mut T) -> Result<(), E>
161 where
162 Self: Clone + Sized + 'static,
163 {
164 self.clone().into_fn()
165 }
166
167 /// Converts this runnable into a callable returning unit.
168 ///
169 /// # Returns
170 ///
171 /// A `BoxCallableWith<T, (), E>` that runs this task and returns unit on
172 /// success.
173 fn into_callable_with(mut self) -> BoxCallableWith<T, (), E>
174 where
175 Self: Sized + 'static,
176 {
177 BoxCallableWith::new(move |input| self.run_with(input))
178 }
179}
180
181/// Box-based runnable with mutable input.
182///
183/// `BoxRunnableWith<T, E>` stores a
184/// `Box<dyn FnMut(&mut T) -> Result<(), E>>` and can be called repeatedly.
185///
186/// # Author
187///
188/// Haixing Hu
189pub struct BoxRunnableWith<T, E> {
190 /// The stateful closure executed by this runnable.
191 function: Box<dyn FnMut(&mut T) -> Result<(), E>>,
192 /// The optional name of this runnable.
193 name: Option<String>,
194}
195
196impl<T, E> BoxRunnableWith<T, E> {
197 impl_common_new_methods!(
198 (FnMut(&mut T) -> Result<(), E> + 'static),
199 |function| Box::new(function),
200 "runnable-with"
201 );
202
203 impl_common_name_methods!("runnable-with");
204
205 /// Chains another runnable after this runnable succeeds.
206 ///
207 /// # Parameters
208 ///
209 /// * `next` - The runnable to execute after this runnable succeeds.
210 ///
211 /// # Returns
212 ///
213 /// A runnable executing both actions in sequence.
214 #[inline]
215 pub fn and_then<N>(self, next: N) -> BoxRunnableWith<T, E>
216 where
217 N: RunnableWith<T, E> + 'static,
218 T: 'static,
219 E: 'static,
220 {
221 let name = self.name;
222 let mut function = self.function;
223 let mut next = next;
224 BoxRunnableWith::new_with_optional_name(
225 move |input| {
226 function(input)?;
227 next.run_with(input)
228 },
229 name,
230 )
231 }
232
233 /// Runs this runnable before a callable.
234 ///
235 /// The callable is not executed if this runnable returns `Err`.
236 ///
237 /// # Parameters
238 ///
239 /// * `callable` - The callable to execute after this runnable succeeds.
240 ///
241 /// # Returns
242 ///
243 /// A callable producing the second computation's result.
244 #[inline]
245 pub fn then_callable_with<R, C>(self, callable: C) -> BoxCallableWith<T, R, E>
246 where
247 C: crate::tasks::callable_with::CallableWith<T, R, E> + 'static,
248 T: 'static,
249 R: 'static,
250 E: 'static,
251 {
252 let name = self.name;
253 let mut function = self.function;
254 let mut callable = callable;
255 BoxCallableWith::new_with_optional_name(
256 move |input| {
257 function(input)?;
258 callable.call_with(input)
259 },
260 name,
261 )
262 }
263}
264
265impl<T, E> RunnableWith<T, E> for BoxRunnableWith<T, E> {
266 /// Executes the boxed runnable with mutable input.
267 #[inline]
268 fn run_with(&mut self, input: &mut T) -> Result<(), E> {
269 (self.function)(input)
270 }
271
272 impl_box_conversions!(
273 BoxRunnableWith<T, E>,
274 RcRunnableWith,
275 FnMut(&mut T) -> Result<(), E>
276 );
277
278 /// Converts this boxed runnable into a boxed callable while preserving its
279 /// name.
280 #[inline]
281 fn into_callable_with(self) -> BoxCallableWith<T, (), E>
282 where
283 Self: Sized + 'static,
284 {
285 let name = self.name;
286 let mut function = self.function;
287 BoxCallableWith::new_with_optional_name(
288 move |input| {
289 function(input)?;
290 Ok(())
291 },
292 name,
293 )
294 }
295}
296
297/// Single-threaded shared runnable with mutable input.
298///
299/// `RcRunnableWith<T, E>` stores a
300/// `Rc<RefCell<dyn FnMut(&mut T) -> Result<(), E>>>`.
301///
302/// # Author
303///
304/// Haixing Hu
305pub struct RcRunnableWith<T, E> {
306 /// The stateful closure executed by this runnable.
307 function: Rc<RefCell<dyn FnMut(&mut T) -> Result<(), E>>>,
308 /// The optional name of this runnable.
309 name: Option<String>,
310}
311
312impl<T, E> Clone for RcRunnableWith<T, E> {
313 #[inline]
314 fn clone(&self) -> Self {
315 Self {
316 function: Rc::clone(&self.function),
317 name: self.name.clone(),
318 }
319 }
320}
321
322impl<T, E> RcRunnableWith<T, E> {
323 impl_common_new_methods!(
324 (FnMut(&mut T) -> Result<(), E> + 'static),
325 |function| Rc::new(RefCell::new(function)),
326 "runnable-with"
327 );
328
329 impl_common_name_methods!("runnable-with");
330}
331
332impl<T, E> RunnableWith<T, E> for RcRunnableWith<T, E> {
333 /// Executes the shared runnable with mutable input.
334 #[inline]
335 fn run_with(&mut self, input: &mut T) -> Result<(), E> {
336 (self.function.borrow_mut())(input)
337 }
338
339 impl_rc_conversions!(
340 RcRunnableWith<T, E>,
341 BoxRunnableWith,
342 FnMut(input: &mut T) -> Result<(), E>
343 );
344}
345
346/// Thread-safe shared runnable with mutable input.
347///
348/// `ArcRunnableWith<T, E>` stores an
349/// `Arc<Mutex<dyn FnMut(&mut T) -> Result<(), E> + Send>>`.
350///
351/// # Author
352///
353/// Haixing Hu
354pub struct ArcRunnableWith<T, E> {
355 /// The stateful closure executed by this runnable.
356 function: Arc<Mutex<dyn FnMut(&mut T) -> Result<(), E> + Send>>,
357 /// The optional name of this runnable.
358 name: Option<String>,
359}
360
361impl<T, E> Clone for ArcRunnableWith<T, E> {
362 #[inline]
363 fn clone(&self) -> Self {
364 Self {
365 function: Arc::clone(&self.function),
366 name: self.name.clone(),
367 }
368 }
369}
370
371impl<T, E> ArcRunnableWith<T, E> {
372 impl_common_new_methods!(
373 (FnMut(&mut T) -> Result<(), E> + Send + 'static),
374 |function| Arc::new(Mutex::new(function)),
375 "runnable-with"
376 );
377
378 impl_common_name_methods!("runnable-with");
379}
380
381impl<T, E> RunnableWith<T, E> for ArcRunnableWith<T, E> {
382 /// Executes the thread-safe runnable with mutable input.
383 #[inline]
384 fn run_with(&mut self, input: &mut T) -> Result<(), E> {
385 (self.function.lock())(input)
386 }
387
388 impl_arc_conversions!(
389 ArcRunnableWith<T, E>,
390 BoxRunnableWith,
391 RcRunnableWith,
392 FnMut(input: &mut T) -> Result<(), E>
393 );
394}
395
396impl_closure_trait!(
397 RunnableWith<T, E>,
398 run_with,
399 FnMut(input: &mut T) -> Result<(), E>
400);
401
402impl_function_debug_display!(BoxRunnableWith<T, E>);
403impl_function_debug_display!(RcRunnableWith<T, E>);
404impl_function_debug_display!(ArcRunnableWith<T, E>);