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}