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