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