Skip to main content

qubit_function/tasks/callable_with/
box_callable_with.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 `BoxCallableWith` public type.
12
13use crate::{
14    macros::{
15        impl_box_conversions,
16        impl_common_name_methods,
17        impl_common_new_methods,
18    },
19    tasks::{
20        callable_with::{
21            CallableWith,
22            RcCallableWith,
23        },
24        runnable_with::BoxRunnableWith,
25    },
26};
27
28type BoxCallableWithFn<T, R, E> = Box<dyn FnMut(&mut T) -> Result<R, E>>;
29
30/// Box-based callable with mutable input.
31///
32/// `BoxCallableWith<T, R, E>` stores a
33/// `Box<dyn FnMut(&mut T) -> Result<R, E>>` and can be called repeatedly.
34///
35pub struct BoxCallableWith<T, R, E> {
36    /// The stateful closure executed by this callable.
37    pub(super) function: BoxCallableWithFn<T, R, E>,
38    /// The optional name of this callable.
39    pub(super) name: Option<String>,
40}
41
42impl<T, R, E> BoxCallableWith<T, R, E> {
43    impl_common_new_methods!(
44        (FnMut(&mut T) -> Result<R, E> + 'static),
45        |function| Box::new(function),
46        "callable-with"
47    );
48
49    impl_common_name_methods!("callable-with");
50
51    /// Maps the success value of this callable.
52    ///
53    /// # Parameters
54    ///
55    /// * `mapper` - Function that transforms the success value.
56    ///
57    /// # Returns
58    ///
59    /// A new callable with mutable input that applies `mapper` on success.
60    #[inline]
61    pub fn map<U, M>(self, mut mapper: M) -> BoxCallableWith<T, U, E>
62    where
63        M: FnMut(R) -> U + 'static,
64        T: 'static,
65        R: 'static,
66        E: 'static,
67    {
68        let name = self.name;
69        let mut function = self.function;
70        BoxCallableWith::new_with_optional_name(move |input| function(input).map(&mut mapper), name)
71    }
72
73    /// Maps the error value of this callable.
74    ///
75    /// # Parameters
76    ///
77    /// * `mapper` - Function that transforms the error value.
78    ///
79    /// # Returns
80    ///
81    /// A new callable with mutable input that applies `mapper` on failure.
82    #[inline]
83    pub fn map_err<E2, M>(self, mut mapper: M) -> BoxCallableWith<T, R, E2>
84    where
85        M: FnMut(E) -> E2 + 'static,
86        T: 'static,
87        R: 'static,
88        E: 'static,
89    {
90        let name = self.name;
91        let mut function = self.function;
92        BoxCallableWith::new_with_optional_name(
93            move |input| function(input).map_err(&mut mapper),
94            name,
95        )
96    }
97
98    /// Chains another fallible computation after this callable succeeds.
99    ///
100    /// # Parameters
101    ///
102    /// * `next` - Function receiving the success value and mutable input.
103    ///
104    /// # Returns
105    ///
106    /// A new callable that runs `next` only when this callable succeeds.
107    #[inline]
108    pub fn and_then<U, N>(self, next: N) -> BoxCallableWith<T, U, E>
109    where
110        N: FnMut(R, &mut T) -> Result<U, E> + 'static,
111        T: 'static,
112        R: 'static,
113        E: 'static,
114    {
115        let name = self.name;
116        let mut function = self.function;
117        let mut next = next;
118        BoxCallableWith::new_with_optional_name(
119            move |input| {
120                let value = function(input)?;
121                next(value, input)
122            },
123            name,
124        )
125    }
126}
127
128impl<T, R, E> CallableWith<T, R, E> for BoxCallableWith<T, R, E> {
129    /// Executes the boxed callable with mutable input.
130    #[inline]
131    fn call_with(&mut self, input: &mut T) -> Result<R, E> {
132        (self.function)(input)
133    }
134
135    impl_box_conversions!(
136        BoxCallableWith<T, R, E>,
137        RcCallableWith,
138        FnMut(&mut T) -> Result<R, E>
139    );
140
141    /// Converts this boxed callable into a boxed runnable while preserving its
142    /// name.
143    #[inline]
144    fn into_runnable_with(self) -> BoxRunnableWith<T, E>
145    where
146        Self: Sized + 'static,
147    {
148        let name = self.name;
149        let mut function = self.function;
150        BoxRunnableWith::new_with_optional_name(move |input| function(input).map(|_| ()), name)
151    }
152}