Skip to main content

qubit_function/functions/bi_function_once/
fn_bi_function_once_ops.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the `FnBiFunctionOnceOps` public type.
10
11#![allow(unused_imports)]
12
13use super::*;
14
15// ============================================================================
16// FnBiFunctionOnceOps - Extension trait for FnOnce(&T, &U) -> R bi-functions
17// ============================================================================
18
19/// Extension trait for closures implementing `FnOnce(&T, &U) -> R`
20///
21/// Provides composition methods (`and_then`, `when`) for one-time use
22/// bi-function closures and function pointers without requiring explicit
23/// wrapping in `BoxBiFunctionOnce`.
24///
25/// This trait is automatically implemented for all closures and function
26/// pointers that implement `FnOnce(&T, &U) -> R`.
27///
28/// # Design Rationale
29///
30/// While closures automatically implement `BiFunctionOnce<T, U, R>` through
31/// blanket implementation, they don't have access to instance methods like
32/// `and_then` and `when`. This extension trait provides those methods,
33/// returning `BoxBiFunctionOnce` for maximum flexibility.
34///
35/// # Examples
36///
37/// ## Chain composition with and_then
38///
39/// ```rust
40/// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps, FunctionOnce};
41///
42/// let add = |x: &i32, y: &i32| *x + *y;
43/// let double = |x: &i32| x * 2;
44///
45/// let composed = add.and_then(double);
46/// assert_eq!(composed.apply(&3, &5), 16); // (3 + 5) * 2
47/// ```
48///
49/// ## Conditional execution with when
50///
51/// ```rust
52/// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
53///
54/// let add = |x: &i32, y: &i32| *x + *y;
55/// let multiply = |x: &i32, y: &i32| *x * *y;
56///
57/// let conditional = add.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
58/// assert_eq!(conditional.apply(&5, &3), 8); // when branch executed
59/// ```
60///
61/// # Author
62///
63/// Haixing Hu
64pub trait FnBiFunctionOnceOps<T, U, R>: FnOnce(&T, &U) -> R + Sized {
65    /// Chain composition - applies self first, then after
66    ///
67    /// Creates a new bi-function that applies this bi-function first,
68    /// then applies the after function to the result. Consumes self and
69    /// returns a `BoxBiFunctionOnce`.
70    ///
71    /// # Type Parameters
72    ///
73    /// * `S` - The output type of the after function
74    /// * `F` - The type of the after function (must implement FunctionOnce<R, S>)
75    ///
76    /// # Parameters
77    ///
78    /// * `after` - The function to apply after self. **Note: This parameter
79    ///   is passed by value and will transfer ownership.** Since this is a
80    ///   `FnOnce` bi-function, the parameter will be consumed. Can be:
81    ///   - A closure: `|x: R| -> S`
82    ///   - A function pointer: `fn(R) -> S`
83    ///   - A `BoxFunctionOnce<R, S>`
84    ///   - Any type implementing `FunctionOnce<R, S>`
85    ///
86    /// # Returns
87    ///
88    /// A new `BoxBiFunctionOnce<T, U, S>` representing the composition
89    ///
90    /// # Examples
91    ///
92    /// ```rust
93    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps,
94    ///     BoxFunctionOnce};
95    ///
96    /// let add = |x: &i32, y: &i32| *x + *y;
97    /// let to_string = BoxFunctionOnce::new(|x: &i32| x.to_string());
98    ///
99    /// // to_string is moved and consumed
100    /// let composed = add.and_then(to_string);
101    /// assert_eq!(composed.apply(&20, &22), "42");
102    /// // to_string.apply(10); // Would not compile - moved
103    /// ```
104    fn and_then<S, F>(self, after: F) -> BoxBiFunctionOnce<T, U, S>
105    where
106        Self: 'static,
107        S: 'static,
108        F: crate::functions::function_once::FunctionOnce<R, S> + 'static,
109        T: 'static,
110        U: 'static,
111        R: 'static,
112    {
113        BoxBiFunctionOnce::new(move |t: &T, u: &U| after.apply(&self(t, u)))
114    }
115
116    /// Creates a conditional bi-function
117    ///
118    /// Returns a bi-function that only executes when a bi-predicate is
119    /// satisfied. You must call `or_else()` to provide an alternative
120    /// bi-function for when the condition is not satisfied.
121    ///
122    /// # Parameters
123    ///
124    /// * `predicate` - The condition to check. **Note: This parameter is passed
125    ///   by value and will transfer ownership.** If you need to preserve the
126    ///   original bi-predicate, clone it first (if it implements `Clone`).
127    ///   Can be:
128    ///   - A closure: `|x: &T, y: &U| -> bool`
129    ///   - A function pointer: `fn(&T, &U) -> bool`
130    ///   - A `BoxBiPredicate<T, U>`
131    ///   - An `RcBiPredicate<T, U>`
132    ///   - An `ArcBiPredicate<T, U>`
133    ///   - Any type implementing `BiPredicate<T, U>`
134    ///
135    /// # Returns
136    ///
137    /// Returns `BoxConditionalBiFunctionOnce<T, U, R>`
138    ///
139    /// # Examples
140    ///
141    /// ## Basic usage with or_else
142    ///
143    /// ```rust
144    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
145    ///
146    /// let add = |x: &i32, y: &i32| *x + *y;
147    /// let multiply = |x: &i32, y: &i32| *x * *y;
148    /// let conditional = add.when(|x: &i32, y: &i32| *x > 0)
149    ///     .or_else(multiply);
150    ///
151    /// assert_eq!(conditional.apply(&5, &3), 8);
152    /// ```
153    ///
154    /// ## Preserving bi-predicate with clone
155    ///
156    /// ```rust
157    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps,
158    ///     RcBiPredicate};
159    ///
160    /// let add = |x: &i32, y: &i32| *x + *y;
161    /// let both_positive = RcBiPredicate::new(|x: &i32, y: &i32|
162    ///     *x > 0 && *y > 0);
163    ///
164    /// // Clone to preserve original bi-predicate
165    /// let conditional = add.when(both_positive.clone())
166    ///     .or_else(|x: &i32, y: &i32| *x * *y);
167    ///
168    /// assert_eq!(conditional.apply(&5, &3), 8);
169    ///
170    /// // Original bi-predicate still usable
171    /// use qubit_function::BiPredicate;
172    /// assert!(both_positive.test(&5, &3));
173    /// ```
174    fn when<P>(self, predicate: P) -> BoxConditionalBiFunctionOnce<T, U, R>
175    where
176        Self: 'static,
177        P: BiPredicate<T, U> + 'static,
178        T: 'static,
179        U: 'static,
180        R: 'static,
181    {
182        BoxBiFunctionOnce::new(self).when(predicate)
183    }
184}
185
186/// Blanket implementation of FnBiFunctionOnceOps for all closures
187///
188/// Automatically implements `FnBiFunctionOnceOps<T, U, R>` for any type that
189/// implements `FnOnce(&T, &U) -> R`.
190///
191/// # Author
192///
193/// Haixing Hu
194impl<T, U, R, F> FnBiFunctionOnceOps<T, U, R> for F
195where
196    F: FnOnce(&T, &U) -> R,
197{
198    // empty
199}