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>);