Skip to main content

qubit_function/functions/bi_function_once/
fn_bi_function_once_ops.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10// qubit-style: allow explicit-imports
11//! Defines the `FnBiFunctionOnceOps` public type.
12
13#![allow(unused_imports)]
14
15use super::*;
16
17// ============================================================================
18// FnBiFunctionOnceOps - Extension trait for FnOnce(&T, &U) -> R bi-functions
19// ============================================================================
20
21/// Extension trait for closures implementing `FnOnce(&T, &U) -> R`
22///
23/// Provides composition methods (`and_then`, `when`) for one-time use
24/// bi-function closures and function pointers without requiring explicit
25/// wrapping in `BoxBiFunctionOnce`.
26///
27/// This trait is automatically implemented for all closures and function
28/// pointers that implement `FnOnce(&T, &U) -> R`.
29///
30/// # Design Rationale
31///
32/// While closures automatically implement `BiFunctionOnce<T, U, R>` through
33/// blanket implementation, they don't have access to instance methods like
34/// `and_then` and `when`. This extension trait provides those methods,
35/// returning `BoxBiFunctionOnce` for maximum flexibility.
36///
37/// # Examples
38///
39/// ## Chain composition with and_then
40///
41/// ```rust
42/// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps, FunctionOnce};
43///
44/// let add = |x: &i32, y: &i32| *x + *y;
45/// let double = |x: &i32| x * 2;
46///
47/// let composed = add.and_then(double);
48/// assert_eq!(composed.apply(&3, &5), 16); // (3 + 5) * 2
49/// ```
50///
51/// ## Conditional execution with when
52///
53/// ```rust
54/// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
55///
56/// let add = |x: &i32, y: &i32| *x + *y;
57/// let multiply = |x: &i32, y: &i32| *x * *y;
58///
59/// let conditional = add.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
60/// assert_eq!(conditional.apply(&5, &3), 8); // when branch executed
61/// ```
62///
63pub trait FnBiFunctionOnceOps<T, U, R>: FnOnce(&T, &U) -> R + Sized {
64    /// Chain composition - applies self first, then after
65    ///
66    /// Creates a new bi-function that applies this bi-function first,
67    /// then applies the after function to the result. Consumes self and
68    /// returns a `BoxBiFunctionOnce`.
69    ///
70    /// # Type Parameters
71    ///
72    /// * `S` - The output type of the after function
73    /// * `F` - The type of the after function (must implement FunctionOnce<R, S>)
74    ///
75    /// # Parameters
76    ///
77    /// * `after` - The function to apply after self. **Note: This parameter
78    ///   is passed by value and will transfer ownership.** Since this is a
79    ///   `FnOnce` bi-function, the parameter will be consumed. Can be:
80    ///   - A closure: `|x: R| -> S`
81    ///   - A function pointer: `fn(R) -> S`
82    ///   - A `BoxFunctionOnce<R, S>`
83    ///   - Any type implementing `FunctionOnce<R, S>`
84    ///
85    /// # Returns
86    ///
87    /// A new `BoxBiFunctionOnce<T, U, S>` representing the composition
88    ///
89    /// # Examples
90    ///
91    /// ```rust
92    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps,
93    ///     BoxFunctionOnce};
94    ///
95    /// let add = |x: &i32, y: &i32| *x + *y;
96    /// let to_string = BoxFunctionOnce::new(|x: &i32| x.to_string());
97    ///
98    /// // to_string is moved and consumed
99    /// let composed = add.and_then(to_string);
100    /// assert_eq!(composed.apply(&20, &22), "42");
101    /// // to_string.apply(10); // Would not compile - moved
102    /// ```
103    fn and_then<S, F>(self, after: F) -> BoxBiFunctionOnce<T, U, S>
104    where
105        Self: 'static,
106        S: 'static,
107        F: crate::functions::function_once::FunctionOnce<R, S> + 'static,
108        T: 'static,
109        U: 'static,
110        R: 'static,
111    {
112        BoxBiFunctionOnce::new(move |t: &T, u: &U| after.apply(&self(t, u)))
113    }
114
115    /// Creates a conditional bi-function
116    ///
117    /// Returns a bi-function that only executes when a bi-predicate is
118    /// satisfied. You must call `or_else()` to provide an alternative
119    /// bi-function for when the condition is not satisfied.
120    ///
121    /// # Parameters
122    ///
123    /// * `predicate` - The condition to check. **Note: This parameter is passed
124    ///   by value and will transfer ownership.** If you need to preserve the
125    ///   original bi-predicate, clone it first (if it implements `Clone`).
126    ///   Can be:
127    ///   - A closure: `|x: &T, y: &U| -> bool`
128    ///   - A function pointer: `fn(&T, &U) -> bool`
129    ///   - A `BoxBiPredicate<T, U>`
130    ///   - An `RcBiPredicate<T, U>`
131    ///   - An `ArcBiPredicate<T, U>`
132    ///   - Any type implementing `BiPredicate<T, U>`
133    ///
134    /// # Returns
135    ///
136    /// Returns `BoxConditionalBiFunctionOnce<T, U, R>`
137    ///
138    /// # Examples
139    ///
140    /// ## Basic usage with or_else
141    ///
142    /// ```rust
143    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps};
144    ///
145    /// let add = |x: &i32, y: &i32| *x + *y;
146    /// let multiply = |x: &i32, y: &i32| *x * *y;
147    /// let conditional = add.when(|x: &i32, y: &i32| *x > 0)
148    ///     .or_else(multiply);
149    ///
150    /// assert_eq!(conditional.apply(&5, &3), 8);
151    /// ```
152    ///
153    /// ## Preserving bi-predicate with clone
154    ///
155    /// ```rust
156    /// use qubit_function::{BiFunctionOnce, FnBiFunctionOnceOps,
157    ///     RcBiPredicate};
158    ///
159    /// let add = |x: &i32, y: &i32| *x + *y;
160    /// let both_positive = RcBiPredicate::new(|x: &i32, y: &i32|
161    ///     *x > 0 && *y > 0);
162    ///
163    /// // Clone to preserve original bi-predicate
164    /// let conditional = add.when(both_positive.clone())
165    ///     .or_else(|x: &i32, y: &i32| *x * *y);
166    ///
167    /// assert_eq!(conditional.apply(&5, &3), 8);
168    ///
169    /// // Original bi-predicate still usable
170    /// use qubit_function::BiPredicate;
171    /// assert!(both_positive.test(&5, &3));
172    /// ```
173    fn when<P>(self, predicate: P) -> BoxConditionalBiFunctionOnce<T, U, R>
174    where
175        Self: 'static,
176        P: BiPredicate<T, U> + 'static,
177        T: 'static,
178        U: 'static,
179        R: 'static,
180    {
181        BoxBiFunctionOnce::new(self).when(predicate)
182    }
183}
184
185/// Blanket implementation of FnBiFunctionOnceOps for all closures
186///
187/// Automatically implements `FnBiFunctionOnceOps<T, U, R>` for any type that
188/// implements `FnOnce(&T, &U) -> R`.
189///
190impl<T, U, R, F> FnBiFunctionOnceOps<T, U, R> for F
191where
192    F: FnOnce(&T, &U) -> R,
193{
194    // empty
195}