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