Skip to main content

qubit_function/functions/
bi_function_once.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9
10//! # BiFunctionOnce Types
11//!
12//! Provides Rust implementations of consuming bi-function traits similar to
13//! Rust's `FnOnce(&T, &U) -> R` trait, but with value-oriented semantics for functional
14//! programming patterns with two input references.
15//!
16//! This module provides the `BiFunctionOnce<T, U, R>` trait and one-time use
17//! implementations:
18//!
19//! - [`BoxBiFunctionOnce`]: Single ownership, one-time use
20//!
21//! # Author
22//!
23//! Haixing Hu
24use crate::macros::{
25    impl_box_once_conversions,
26    impl_closure_once_trait,
27};
28use crate::predicates::bi_predicate::{
29    BiPredicate,
30    BoxBiPredicate,
31};
32use crate::{
33    functions::function_once::FunctionOnce,
34    functions::macros::{
35        impl_box_conditional_function,
36        impl_box_function_methods,
37        impl_conditional_function_debug_display,
38        impl_function_common_methods,
39        impl_function_constant_method,
40        impl_function_debug_display,
41    },
42};
43
44// ============================================================================
45// Core Trait
46// ============================================================================
47
48/// BiFunctionOnce trait - consuming bi-function that takes references
49///
50/// Defines the behavior of a consuming bi-function: computing a value of
51/// type `R` from references to types `T` and `U` by taking ownership of self.
52/// This trait is analogous to `FnOnce(&T, &U) -> R`.
53///
54/// # Type Parameters
55///
56/// * `T` - The type of the first input value (borrowed)
57/// * `U` - The type of the second input value (borrowed)
58/// * `R` - The type of the output value
59///
60/// # Author
61///
62/// Haixing Hu
63pub trait BiFunctionOnce<T, U, R> {
64    /// Computes output from two input references, consuming self
65    ///
66    /// # Parameters
67    ///
68    /// * `first` - Reference to the first input value
69    /// * `second` - Reference to the second input value
70    ///
71    /// # Returns
72    ///
73    /// The computed output value
74    fn apply(self, first: &T, second: &U) -> R;
75
76    /// Converts to BoxBiFunctionOnce
77    ///
78    /// **⚠️ Consumes `self`**: The original bi-function becomes unavailable
79    /// after calling this method.
80    ///
81    /// # Returns
82    ///
83    /// Returns `BoxBiFunctionOnce<T, U, R>`
84    fn into_box(self) -> BoxBiFunctionOnce<T, U, R>
85    where
86        Self: Sized + 'static,
87    {
88        BoxBiFunctionOnce::new(move |t: &T, u: &U| self.apply(t, u))
89    }
90
91    /// Converts bi-function to a closure
92    ///
93    /// **⚠️ Consumes `self`**: The original bi-function becomes unavailable
94    /// after calling this method.
95    ///
96    /// # Returns
97    ///
98    /// Returns a closure that implements `FnOnce(&T, &U) -> R`
99    fn into_fn(self) -> impl FnOnce(&T, &U) -> R
100    where
101        Self: Sized + 'static,
102    {
103        move |t: &T, u: &U| self.apply(t, u)
104    }
105
106    /// Converts bi-function to a boxed function pointer
107    ///
108    /// **📌 Borrows `&self`**: The original bi-function remains usable
109    /// after calling this method.
110    ///
111    /// # Returns
112    ///
113    /// Returns a boxed function pointer that implements `FnOnce(&T, &U) -> R`
114    ///
115    /// # Examples
116    ///
117    /// ```rust
118    /// use qubit_function::BiFunctionOnce;
119    ///
120    /// let add = |x: &i32, y: &i32| *x + *y;
121    /// let func = add.to_box();
122    /// assert_eq!(func.apply(&20, &22), 42);
123    /// ```
124    fn to_box(&self) -> BoxBiFunctionOnce<T, U, R>
125    where
126        Self: Clone + 'static,
127    {
128        self.clone().into_box()
129    }
130
131    /// Converts bi-function to a closure
132    ///
133    /// **📌 Borrows `&self`**: The original bi-function remains usable
134    /// after calling this method.
135    ///
136    /// # Returns
137    ///
138    /// Returns a closure that implements `FnOnce(&T, &U) -> R`
139    ///
140    /// # Examples
141    ///
142    /// ```rust
143    /// use qubit_function::BiFunctionOnce;
144    ///
145    /// let add = |x: &i32, y: &i32| *x + *y;
146    /// let func = add.to_fn();
147    /// assert_eq!(func(&20, &22), 42);
148    /// ```
149    fn to_fn(&self) -> impl FnOnce(&T, &U) -> R
150    where
151        Self: Clone + 'static,
152    {
153        self.clone().into_fn()
154    }
155}
156
157// ============================================================================
158// BoxBiFunctionOnce - Box<dyn FnOnce(&T, &U) -> R>
159// ============================================================================
160
161/// BoxBiFunctionOnce - consuming bi-function wrapper based on
162/// `Box<dyn FnOnce>`
163///
164/// A bi-function wrapper that provides single ownership with one-time use
165/// semantics. Consumes self and borrows both input values.
166///
167/// # Features
168///
169/// - **Based on**: `Box<dyn FnOnce(&T, &U) -> R>`
170/// - **Ownership**: Single ownership, cannot be cloned
171/// - **Reusability**: Can only be called once (consumes self)
172/// - **Thread Safety**: Not thread-safe (no `Send + Sync` requirement)
173///
174/// # Author
175///
176/// Haixing Hu
177pub struct BoxBiFunctionOnce<T, U, R> {
178    function: Box<dyn FnOnce(&T, &U) -> R>,
179    name: Option<String>,
180}
181
182// Implement BoxBiFunctionOnce
183impl<T, U, R> BoxBiFunctionOnce<T, U, R> {
184    // Generate new(), new_with_name(), new_with_optional_name(), name(), set_name()
185    impl_function_common_methods!(
186        BoxBiFunctionOnce<T, U, R>,
187        (FnOnce(&T, &U) -> R + 'static),
188        |f| Box::new(f)
189    );
190
191    // Generate when(), and_then()
192    impl_box_function_methods!(
193        BoxBiFunctionOnce<T, U, R>,
194        BoxConditionalBiFunctionOnce,
195        FunctionOnce
196    );
197}
198
199// Implement BiFunctionOnce trait for BoxBiFunctionOnce
200impl<T, U, R> BiFunctionOnce<T, U, R> for BoxBiFunctionOnce<T, U, R> {
201    fn apply(self, first: &T, second: &U) -> R {
202        (self.function)(first, second)
203    }
204
205    // Generate into_box(), into_fn(), to_box()
206    impl_box_once_conversions!(
207        BoxBiFunctionOnce<T, U, R>,
208        BiFunctionOnce,
209        FnOnce(&T, &U) -> R
210    );
211}
212
213// Implement constant method for BoxBiFunctionOnce
214impl_function_constant_method!(BoxBiFunctionOnce<T, U, R>);
215
216// Use macro to generate Debug and Display implementations
217impl_function_debug_display!(BoxBiFunctionOnce<T, U, R>);
218
219// ============================================================================
220// Blanket implementation for standard FnOnce trait
221// ============================================================================
222
223// Implement BiFunctionOnce for all FnOnce(&T, &U) -> R using macro
224impl_closure_once_trait!(
225    BiFunctionOnce<T, U, R>,
226    apply,
227    BoxBiFunctionOnce,
228    FnOnce(first: &T, second: &U) -> R
229);
230
231// ============================================================================
232// FnBiFunctionOnceOps - Extension trait for FnOnce(&T, &U) -> R bi-functions
233// ============================================================================
234
235/// Extension trait for closures implementing `FnOnce(&T, &U) -> R`
236///
237/// Provides composition methods (`and_then`, `when`) for one-time use
238/// bi-function closures and function pointers without requiring explicit
239/// wrapping in `BoxBiFunctionOnce`.
240///
241/// This trait is automatically implemented for all closures and function
242/// pointers that implement `FnOnce(&T, &U) -> R`.
243///
244/// # Design Rationale
245///
246/// While closures automatically implement `BiFunctionOnce<T, U, R>` through
247/// blanket implementation, they don't have access to instance methods like
248/// `and_then` and `when`. This extension trait provides those methods,
249/// returning `BoxBiFunctionOnce` for maximum flexibility.
250///
251/// # Examples
252///
253/// ## Chain composition with and_then
254///
255/// ```rust
256/// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
257///
258/// let add = |x: &i32, y: &i32| *x + *y;
259/// let double = |x: i32| x * 2;
260///
261/// let composed = add.and_then(double);
262/// assert_eq!(composed.apply(&3, &5), 16); // (3 + 5) * 2
263/// ```
264///
265/// ## Conditional execution with when
266///
267/// ```rust
268/// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
269///
270/// let add = |x: &i32, y: &i32| *x + *y;
271/// let multiply = |x: &i32, y: &i32| *x * *y;
272///
273/// let conditional = add.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
274/// assert_eq!(conditional.apply(&5, &3), 8); // when branch executed
275/// ```
276///
277/// # Author
278///
279/// Haixing Hu
280pub trait FnBiFunctionOnceOps<T, U, R>: FnOnce(&T, &U) -> R + Sized {
281    /// Chain composition - applies self first, then after
282    ///
283    /// Creates a new bi-function that applies this bi-function first,
284    /// then applies the after function to the result. Consumes self and
285    /// returns a `BoxBiFunctionOnce`.
286    ///
287    /// # Type Parameters
288    ///
289    /// * `S` - The output type of the after function
290    /// * `F` - The type of the after function (must implement FunctionOnce<R, S>)
291    ///
292    /// # Parameters
293    ///
294    /// * `after` - The function to apply after self. **Note: This parameter
295    ///   is passed by value and will transfer ownership.** Since this is a
296    ///   `FnOnce` bi-function, the parameter will be consumed. Can be:
297    ///   - A closure: `|x: R| -> S`
298    ///   - A function pointer: `fn(R) -> S`
299    ///   - A `BoxFunctionOnce<R, S>`
300    ///   - Any type implementing `FunctionOnce<R, S>`
301    ///
302    /// # Returns
303    ///
304    /// A new `BoxBiFunctionOnce<T, U, S>` representing the composition
305    ///
306    /// # Examples
307    ///
308    /// ```rust
309    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps,
310    ///     BoxFunctionOnce};
311    ///
312    /// let add = |x: &i32, y: &i32| *x + *y;
313    /// let to_string = BoxFunctionOnce::new(|x: i32| x.to_string());
314    ///
315    /// // to_string is moved and consumed
316    /// let composed = add.and_then(to_string);
317    /// assert_eq!(composed.apply(&20, &22), "42");
318    /// // to_string.apply(10); // Would not compile - moved
319    /// ```
320    fn and_then<S, F>(self, after: F) -> BoxBiFunctionOnce<T, U, S>
321    where
322        Self: 'static,
323        S: 'static,
324        F: crate::functions::function_once::FunctionOnce<R, S> + 'static,
325        T: 'static,
326        U: 'static,
327        R: 'static,
328    {
329        BoxBiFunctionOnce::new(move |t: &T, u: &U| after.apply(&self(t, u)))
330    }
331
332    /// Creates a conditional bi-function
333    ///
334    /// Returns a bi-function that only executes when a bi-predicate is
335    /// satisfied. You must call `or_else()` to provide an alternative
336    /// bi-function for when the condition is not satisfied.
337    ///
338    /// # Parameters
339    ///
340    /// * `predicate` - The condition to check. **Note: This parameter is passed
341    ///   by value and will transfer ownership.** If you need to preserve the
342    ///   original bi-predicate, clone it first (if it implements `Clone`).
343    ///   Can be:
344    ///   - A closure: `|x: &T, y: &U| -> bool`
345    ///   - A function pointer: `fn(&T, &U) -> bool`
346    ///   - A `BoxBiPredicate<T, U>`
347    ///   - An `RcBiPredicate<T, U>`
348    ///   - An `ArcBiPredicate<T, U>`
349    ///   - Any type implementing `BiPredicate<T, U>`
350    ///
351    /// # Returns
352    ///
353    /// Returns `BoxConditionalBiFunctionOnce<T, U, R>`
354    ///
355    /// # Examples
356    ///
357    /// ## Basic usage with or_else
358    ///
359    /// ```rust
360    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
361    ///
362    /// let add = |x: &i32, y: &i32| *x + *y;
363    /// let multiply = |x: &i32, y: &i32| *x * *y;
364    /// let conditional = add.when(|x: &i32, y: &i32| *x > 0)
365    ///     .or_else(multiply);
366    ///
367    /// assert_eq!(conditional.apply(&5, &3), 8);
368    /// ```
369    ///
370    /// ## Preserving bi-predicate with clone
371    ///
372    /// ```rust
373    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps,
374    ///     RcBiPredicate};
375    ///
376    /// let add = |x: &i32, y: &i32| *x + *y;
377    /// let both_positive = RcBiPredicate::new(|x: &i32, y: &i32|
378    ///     *x > 0 && *y > 0);
379    ///
380    /// // Clone to preserve original bi-predicate
381    /// let conditional = add.when(both_positive.clone())
382    ///     .or_else(|x: &i32, y: &i32| *x * *y);
383    ///
384    /// assert_eq!(conditional.apply(&5, &3), 8);
385    ///
386    /// // Original bi-predicate still usable
387    /// assert!(both_positive.test(&5, &3));
388    /// ```
389    fn when<P>(self, predicate: P) -> BoxConditionalBiFunctionOnce<T, U, R>
390    where
391        Self: 'static,
392        P: BiPredicate<T, U> + 'static,
393        T: 'static,
394        U: 'static,
395        R: 'static,
396    {
397        BoxBiFunctionOnce::new(self).when(predicate)
398    }
399}
400
401/// Blanket implementation of FnBiFunctionOnceOps for all closures
402///
403/// Automatically implements `FnBiFunctionOnceOps<T, U, R>` for any type that
404/// implements `FnOnce(&T, &U) -> R`.
405///
406/// # Author
407///
408/// Haixing Hu
409impl<T, U, R, F> FnBiFunctionOnceOps<T, U, R> for F
410where
411    F: FnOnce(&T, &U) -> R,
412{
413    // empty
414}
415
416// ============================================================================
417// BoxConditionalBiFunctionOnce - Box-based Conditional BiFunction
418// ============================================================================
419
420/// BoxConditionalBiFunctionOnce struct
421///
422/// A conditional consuming bi-function that only executes when a bi-predicate
423/// is satisfied. Uses `BoxBiFunctionOnce` and `BoxBiPredicate` for single
424/// ownership semantics.
425///
426/// This type is typically created by calling `BoxBiFunctionOnce::when()` and
427/// is designed to work with the `or_else()` method to create if-then-else logic.
428///
429/// # Features
430///
431/// - **Single Ownership**: Not cloneable, consumes `self` on use
432/// - **One-time Use**: Can only be called once
433/// - **Conditional Execution**: Only computes when bi-predicate returns `true`
434/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
435///
436/// # Examples
437///
438/// ## With or_else Branch
439///
440/// ```rust
441/// use qubit_function::{BiFunctionOnce, BoxBiFunctionOnce};
442///
443/// let add = BoxBiFunctionOnce::new(|x: &i32, y: &i32| *x + *y);
444/// let multiply = BoxBiFunctionOnce::new(|x: &i32, y: &i32| *x * *y);
445/// let conditional = add.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
446/// assert_eq!(conditional.apply(&5, &3), 8); // when branch executed
447///
448/// let add2 = BoxBiFunctionOnce::new(|x: &i32, y: &i32| *x + *y);
449/// let multiply2 = BoxBiFunctionOnce::new(|x: &i32, y: &i32| *x * *y);
450/// let conditional2 = add2.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply2);
451/// assert_eq!(conditional2.apply(&-5, &3), -15); // or_else branch executed
452/// ```
453///
454/// # Author
455///
456/// Haixing Hu
457pub struct BoxConditionalBiFunctionOnce<T, U, R> {
458    function: BoxBiFunctionOnce<T, U, R>,
459    predicate: BoxBiPredicate<T, U>,
460}
461
462// Implement BoxConditionalBiFunctionOnce
463impl_box_conditional_function!(
464    BoxConditionalBiFunctionOnce<T, U, R>,
465    BoxBiFunctionOnce,
466    BiFunctionOnce
467);
468
469// Use macro to generate Debug and Display implementations
470impl_conditional_function_debug_display!(BoxConditionalBiFunctionOnce<T, U, R>);