Skip to main content

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>);