prism3_function/functions/
bi_function_once.rs

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