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