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