Skip to main content

qubit_function/tasks/callable/
box_callable.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// qubit-style: allow explicit-imports
11//! Defines the `BoxCallable` public type.
12
13use crate::{
14    macros::{
15        impl_box_conversions,
16        impl_common_name_methods,
17        impl_common_new_methods,
18    },
19    suppliers::supplier::Supplier,
20    tasks::{
21        callable::{
22            Callable,
23            RcCallable,
24        },
25        callable_once::LocalBoxCallableOnce,
26        runnable::BoxRunnable,
27    },
28};
29
30// ============================================================================
31// BoxCallable
32// ============================================================================
33
34/// Box-based callable.
35///
36/// `BoxCallable<R, E>` stores a `Box<dyn FnMut() -> Result<R, E>>` and can be
37/// called repeatedly. It is the boxed concrete implementation of
38/// [`Callable`].
39///
40/// # Type Parameters
41///
42/// * `R` - The success value returned by the computation.
43/// * `E` - The error value returned when the computation fails.
44///
45/// # Example
46///
47/// ```rust
48/// use qubit_function::{BoxCallable, Callable};
49///
50/// let mut task = BoxCallable::new(|| Ok::<i32, String>(42));
51/// assert_eq!(task.call().expect("call should succeed"), 42);
52/// ```
53///
54pub struct BoxCallable<R, E> {
55    /// The stateful closure executed by this callable.
56    pub(super) function: Box<dyn FnMut() -> Result<R, E>>,
57    /// The optional name of this callable.
58    pub(super) name: Option<String>,
59}
60
61impl<R, E> BoxCallable<R, E> {
62    impl_common_new_methods!(
63        (FnMut() -> Result<R, E> + 'static),
64        |function| Box::new(function),
65        "callable"
66    );
67
68    /// Creates a boxed callable from a reusable supplier.
69    ///
70    /// This is an explicit bridge from `Supplier<Result<R, E>>` to
71    /// `Callable<R, E>`.
72    ///
73    /// # Parameters
74    ///
75    /// * `supplier` - The supplier that produces the callable result.
76    ///
77    /// # Returns
78    ///
79    /// A new `BoxCallable<R, E>`.
80    #[inline]
81    pub fn from_supplier<S>(supplier: S) -> Self
82    where
83        S: Supplier<Result<R, E>> + 'static,
84    {
85        Self::new(move || supplier.get())
86    }
87
88    impl_common_name_methods!("callable");
89
90    /// Maps the success value of this callable.
91    ///
92    /// # Parameters
93    ///
94    /// * `mapper` - Function that transforms the success value.
95    ///
96    /// # Returns
97    ///
98    /// A new callable that applies `mapper` when this callable succeeds.
99    #[inline]
100    pub fn map<U, M>(self, mut mapper: M) -> BoxCallable<U, E>
101    where
102        M: FnMut(R) -> U + 'static,
103        R: 'static,
104        E: 'static,
105    {
106        let name = self.name;
107        let mut function = self.function;
108        BoxCallable::new_with_optional_name(move || function().map(&mut mapper), name)
109    }
110
111    /// Maps the error value of this callable.
112    ///
113    /// # Parameters
114    ///
115    /// * `mapper` - Function that transforms the error value.
116    ///
117    /// # Returns
118    ///
119    /// A new callable that applies `mapper` when this callable fails.
120    #[inline]
121    pub fn map_err<E2, M>(self, mut mapper: M) -> BoxCallable<R, E2>
122    where
123        M: FnMut(E) -> E2 + 'static,
124        R: 'static,
125        E: 'static,
126    {
127        let name = self.name;
128        let mut function = self.function;
129        BoxCallable::new_with_optional_name(move || function().map_err(&mut mapper), name)
130    }
131
132    /// Chains another fallible computation after this callable succeeds.
133    ///
134    /// # Parameters
135    ///
136    /// * `next` - Function that receives the success value and returns the next
137    ///   result.
138    ///
139    /// # Returns
140    ///
141    /// A new callable that runs `next` only when this callable succeeds.
142    #[inline]
143    pub fn and_then<U, N>(self, next: N) -> BoxCallable<U, E>
144    where
145        N: FnMut(R) -> Result<U, E> + 'static,
146        R: 'static,
147        E: 'static,
148    {
149        let name = self.name;
150        let mut function = self.function;
151        let mut next = next;
152        BoxCallable::new_with_optional_name(
153            move || {
154                let value = function()?;
155                next(value)
156            },
157            name,
158        )
159    }
160}
161
162impl<R, E> Callable<R, E> for BoxCallable<R, E> {
163    /// Executes the boxed callable.
164    #[inline]
165    fn call(&mut self) -> Result<R, E> {
166        (self.function)()
167    }
168
169    impl_box_conversions!(
170        BoxCallable<R, E>,
171        RcCallable,
172        FnMut() -> Result<R, E>
173    );
174
175    /// Converts this boxed callable into a local boxed one-time callable while
176    /// preserving its name.
177    #[inline]
178    fn into_local_once(self) -> LocalBoxCallableOnce<R, E>
179    where
180        Self: Sized + 'static,
181    {
182        let name = self.name;
183        let function = self.function;
184        LocalBoxCallableOnce::new_with_optional_name(function, name)
185    }
186
187    /// Converts this boxed callable into a boxed runnable while preserving its
188    /// name.
189    #[inline]
190    fn into_runnable(self) -> BoxRunnable<E>
191    where
192        Self: Sized + 'static,
193    {
194        let name = self.name;
195        let mut function = self.function;
196        BoxRunnable::new_with_optional_name(move || function().map(|_| ()), name)
197    }
198}