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