Skip to main content

qubit_function/functions/bi_mutating_function/
fn_bi_mutating_function_ops.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 `FnBiMutatingFunctionOps` public type.
12
13#![allow(unused_imports)]
14
15use super::*;
16
17// ============================================================================
18// FnBiMutatingFunctionOps - Extension trait for Fn(&mut T, &mut U) -> R bi-functions
19// ============================================================================
20
21/// Extension trait for closures implementing `Fn(&mut T, &mut U) -> R`
22///
23/// Provides composition methods (`and_then`, `when`) for bi-mutating-function
24/// closures and function pointers without requiring explicit wrapping in
25/// `BoxBiMutatingFunction`.
26///
27/// This trait is automatically implemented for all closures and function
28/// pointers that implement `Fn(&mut T, &mut U) -> R`.
29///
30/// # Design Rationale
31///
32/// While closures automatically implement `BiMutatingFunction<T, U, R>` through
33/// blanket implementation, they don't have access to instance methods like
34/// `and_then` and `when`. This extension trait provides those methods,
35/// returning `BoxBiMutatingFunction` for maximum flexibility.
36///
37/// # Examples
38///
39/// ## Chain composition with and_then
40///
41/// ```rust
42/// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
43///
44/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
45///     let temp = *x;
46///     *x = *y;
47///     *y = temp;
48///     *x + *y
49/// };
50/// let double = |x: &i32| x * 2;
51///
52/// let composed = swap_and_sum.and_then(double);
53/// let mut a = 3;
54/// let mut b = 5;
55/// assert_eq!(composed.apply(&mut a, &mut b), 16); // (5 + 3) * 2 = 16
56/// ```
57///
58/// ## Conditional execution with when
59///
60/// ```rust
61/// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
62///
63/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
64///     let temp = *x;
65///     *x = *y;
66///     *y = temp;
67///     *x + *y
68/// };
69/// let multiply = |x: &mut i32, y: &mut i32| {
70///     *x *= *y;
71///     *x
72/// };
73///
74/// let conditional = swap_and_sum.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
75///
76/// let mut a = 5;
77/// let mut b = 3;
78/// assert_eq!(conditional.apply(&mut a, &mut b), 8);   // swap_and_sum: (3 + 5)
79///
80/// let mut a = -5;
81/// let mut b = 3;
82/// assert_eq!(conditional.apply(&mut a, &mut b), -15); // multiply: (-5 * 3)
83/// ```
84///
85pub trait FnBiMutatingFunctionOps<T, U, R>: Fn(&mut T, &mut U) -> R + Sized {
86    /// Chain composition - applies self first, then after
87    ///
88    /// Creates a new bi-mutating-function that applies this bi-mutating-function first,
89    /// then applies the after function to the result. Consumes self and
90    /// returns a `BoxBiMutatingFunction`.
91    ///
92    /// # Type Parameters
93    ///
94    /// * `S` - The output type of the after function
95    /// * `F` - The type of the after function (must implement Function<R, S>)
96    ///
97    /// # Parameters
98    ///
99    /// * `after` - The function to apply after self. **Note: This parameter
100    ///   is passed by value and will transfer ownership.** If you need to
101    ///   preserve the original function, clone it first (if it implements
102    ///   `Clone`). Can be:
103    ///   - A closure: `|x: R| -> S`
104    ///   - A function pointer: `fn(R) -> S`
105    ///   - A `BoxFunction<R, S>`
106    ///   - An `RcFunction<R, S>`
107    ///   - An `ArcFunction<R, S>`
108    ///   - Any type implementing `Function<R, S>`
109    ///
110    /// # Returns
111    ///
112    /// A new `BoxBiMutatingFunction<T, U, S>` representing the composition
113    ///
114    /// # Examples
115    ///
116    /// ## Direct value passing (ownership transfer)
117    ///
118    /// ```rust
119    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
120    ///     BoxFunction, Function};
121    ///
122    /// let swap = |x: &mut i32, y: &mut i32| {
123    ///     let temp = *x;
124    ///     *x = *y;
125    ///     *y = temp;
126    ///     *x + *y
127    /// };
128    /// let to_string = BoxFunction::new(|x: &i32| x.to_string());
129    ///
130    /// // to_string is moved here
131    /// let composed = swap.and_then(to_string);
132    /// let mut a = 20;
133    /// let mut b = 22;
134    /// assert_eq!(composed.apply(&mut a, &mut b), "42");
135    /// // to_string.apply(10); // Would not compile - moved
136    /// ```
137    ///
138    /// ## Preserving original with clone
139    ///
140    /// ```rust
141    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
142    ///     Function, RcFunction};
143    ///
144    /// let swap = |x: &mut i32, y: &mut i32| {
145    ///     let temp = *x;
146    ///     *x = *y;
147    ///     *y = temp;
148    ///     *x + *y
149    /// };
150    /// let to_string = RcFunction::new(|x: &i32| x.to_string());
151    ///
152    /// // Clone to preserve original
153    /// let composed = swap.and_then(to_string.clone());
154    /// let mut a = 20;
155    /// let mut b = 22;
156    /// assert_eq!(composed.apply(&mut a, &mut b), "42");
157    ///
158    /// // Original still usable
159    /// assert_eq!(to_string.apply(&10), "10");
160    /// ```
161    fn and_then<S, F>(self, after: F) -> BoxBiMutatingFunction<T, U, S>
162    where
163        Self: 'static,
164        S: 'static,
165        F: crate::functions::function::Function<R, S> + 'static,
166        T: 'static,
167        U: 'static,
168        R: 'static,
169    {
170        BoxBiMutatingFunction::new(move |t: &mut T, u: &mut U| after.apply(&self(t, u)))
171    }
172
173    /// Creates a conditional bi-mutating-function
174    ///
175    /// Returns a bi-mutating-function that only executes when a bi-predicate is
176    /// satisfied. You must call `or_else()` to provide an alternative
177    /// bi-mutating-function for when the condition is not satisfied.
178    ///
179    /// # Parameters
180    ///
181    /// * `predicate` - The condition to check. **Note: This parameter is passed
182    ///   by value and will transfer ownership.** If you need to preserve the
183    ///   original bi-predicate, clone it first (if it implements `Clone`).
184    ///   Can be:
185    ///   - A closure: `|x: &mut T, y: &mut U| -> bool`
186    ///   - A function pointer: `fn(&mut T, &mut U) -> bool`
187    ///   - A `BoxBiPredicate<T, U>`
188    ///   - An `RcBiPredicate<T, U>`
189    ///   - An `ArcBiPredicate<T, U>`
190    ///   - Any type implementing `BiPredicate<T, U>`
191    ///
192    /// # Returns
193    ///
194    /// Returns `BoxConditionalBiMutatingFunction<T, U, R>`
195    ///
196    /// # Examples
197    ///
198    /// ## Basic usage with or_else
199    ///
200    /// ```rust
201    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
202    ///
203    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
204    ///     let temp = *x;
205    ///     *x = *y;
206    ///     *y = temp;
207    ///     *x + *y
208    /// };
209    /// let multiply = |x: &mut i32, y: &mut i32| {
210    ///     *x *= *y;
211    ///     *x
212    /// };
213    /// let conditional = swap_and_sum.when(|x: &i32, y: &i32| *x > 0)
214    ///     .or_else(multiply);
215    ///
216    /// let mut a = 5;
217    /// let mut b = 3;
218    /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
219    /// ```
220    ///
221    /// ## Preserving bi-predicate with clone
222    ///
223    /// ```rust
224    /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
225    ///     RcBiPredicate};
226    ///
227    /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
228    ///     let temp = *x;
229    ///     *x = *y;
230    ///     *y = temp;
231    ///     *x + *y
232    /// };
233    /// let both_positive = RcBiPredicate::new(|x: &i32, y: &i32|
234    ///     *x > 0 && *y > 0);
235    ///
236    /// // Clone to preserve original bi-predicate
237    /// let conditional = swap_and_sum.when(both_positive.clone())
238    ///     .or_else(|x: &mut i32, y: &mut i32| *x * *y);
239    ///
240    /// let mut a = 5;
241    /// let mut b = 3;
242    /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
243    ///
244    /// // Original bi-predicate still usable
245    /// use qubit_function::BiPredicate;
246    /// let test_a = 5;
247    /// let test_b = 3;
248    /// assert!(both_positive.test(&test_a, &test_b));
249    /// ```
250    fn when<P>(self, predicate: P) -> BoxConditionalBiMutatingFunction<T, U, R>
251    where
252        Self: 'static,
253        P: BiPredicate<T, U> + 'static,
254        T: 'static,
255        U: 'static,
256        R: 'static,
257    {
258        BoxBiMutatingFunction::new(self).when(predicate)
259    }
260}
261
262/// Blanket implementation of FnBiMutatingFunctionOps for all closures
263///
264/// Automatically implements `FnBiMutatingFunctionOps<T, U, R>` for any type that
265/// implements `Fn(&mut T, &mut U) -> R`.
266///
267impl<T, U, R, F> FnBiMutatingFunctionOps<T, U, R> for F where F: Fn(&mut T, &mut U) -> R {}