Skip to main content

qubit_function/tasks/
callable_with.rs

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