prism3_function/functions/macros/fn_ops_trait.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025.
4 * 3-Prism Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # Fn Ops Trait Macro
10//!
11//! Generate extension traits and implementations for closure types
12//!
13//! This macro generates extension traits for closure types that implement
14//! `Fn` or `FnMut`, providing `and_then` and `when` methods without requiring
15//! explicit wrapping as `BoxFunction`, `RcFunction`, or `ArcFunction`.
16//!
17//! # Parameters
18//!
19//! * `$fn_signature` - Closure signature (in parentheses, without constraints)
20//! Examples: `(Fn(&T) -> R)`, `(FnMut(&T) -> R)`, `(FnMut(&mut T) -> R)`
21//! * `$trait_name` - Name of the extension trait (e.g., `FnFunctionOps`,
22//! `FnStatefulFunctionOps`)
23//! * `$box_type` - Box wrapper type (e.g., `BoxFunction`, `BoxStatefulFunction`)
24//! * `$chained_function_trait` - The name of the function trait that chained
25//! after the execution of this function (e.g., Function, BiFunction)
26//! * `$conditional_type` - Conditional function type (e.g., `BoxConditionalFunction`)
27//!
28//! # Implementation Notes
29//!
30//! The macro uses mutable references (`&mut`) uniformly because in Rust,
31//! `&mut T` can be automatically dereferenced to `&T`. This allows both `Fn`
32//! and `FnMut` closures to use the same implementation logic, simplifying
33//! the code and improving performance (avoiding additional boxing operations).
34//!
35//! # Usage Examples
36//!
37//! ```ignore
38//! // Generate extension trait for Fn(&T) -> R
39//! impl_fn_ops_trait!(
40//! (Fn(&T) -> R),
41//! FnFunctionOps,
42//! BoxFunction,
43//! Function,
44//! BoxConditionalFunction
45//! );
46//!
47//! // Generate extension trait for FnMut(&T) -> R
48//! impl_fn_ops_trait!(
49//! (FnMut(&T) -> R),
50//! FnStatefulFunctionOps,
51//! BoxStatefulFunction,
52//! StatefulFunction,
53//! BoxConditionalStatefulFunction
54//! );
55//!
56//! // Generate extension trait for FnMut(&mut T) -> R (consuming functions)
57//! impl_fn_ops_trait!(
58//! (FnMut(&mut T) -> R),
59//! FnMutatingFunctionOps,
60//! BoxMutatingFunction,
61//! MutatingFunction,
62//! BoxConditionalMutatingFunction
63//! );
64//! ```
65//!
66//! # Author
67//!
68//! Haixing Hu
69
70/// Generate extension traits and implementations for closure types
71///
72/// This macro generates an extension trait that provides composition methods
73/// (`and_then`, `when`) for closures implementing the specified
74/// closure trait, without requiring explicit wrapping.
75///
76/// # Unified Implementation Strategy
77///
78/// The macro uses a unified implementation approach, passing intermediate
79/// results using mutable references (`&mut`). This is because:
80/// 1. In Rust, `&mut T` can be automatically dereferenced to `&T`
81/// 2. Avoids code duplication and simplifies the macro implementation
82/// 3. Better performance by avoiding additional boxing operations
83/// 4. Uses `#[allow(unused_mut)]` to suppress unnecessary mutability warnings
84///
85/// # Parameters
86///
87/// * `$fn_signature` - Closure signature (in parentheses, without constraints)
88/// * `$trait_name` - Name of the extension trait
89/// * `$box_type` - Box wrapper type
90/// * `$chained_function_trait` - The name of the function trait that chained
91/// after the execution of this function (e.g., Function, BiFunction)
92/// * `$conditional_type` - Conditional function type
93///
94/// # Generated Code
95///
96/// Generates a trait definition and a blanket implementation, containing:
97/// - `and_then<S, F>` - Chain composition method
98/// - `when<P>` - Conditional execution method
99///
100/// # Examples
101///
102/// ```ignore
103/// // Fn(&T) -> R version
104/// impl_fn_ops_trait!(
105/// (Fn(&T) -> R),
106/// FnFunctionOps,
107/// BoxFunction,
108/// Function,
109/// BoxConditionalFunction
110/// );
111///
112/// // FnMut(&T) -> R version
113/// impl_fn_ops_trait!(
114/// (FnMut(&T) -> R),
115/// FnStatefulFunctionOps,
116/// BoxStatefulFunction,
117/// StatefulFunction,
118/// BoxConditionalStatefulFunction
119/// );
120///
121/// // FnMut(&mut T) -> R version (consuming functions)
122/// impl_fn_ops_trait!(
123/// (FnMut(&mut T) -> R),
124/// FnMutatingFunctionOps,
125/// BoxMutatingFunction,
126/// MutatingFunction,
127/// BoxConditionalMutatingFunction
128/// );
129/// ```
130///
131/// # Author
132///
133/// Haixing Hu
134#[macro_export]
135macro_rules! impl_fn_ops_trait {
136 // Unified implementation - accepts closure signature (without constraints)
137 (
138 ($($fn_signature:tt)+),
139 $trait_name:ident,
140 $box_type:ident,
141 $chained_function_trait:ident,
142 $conditional_type:ident
143 ) => {
144 /// Extension trait for closures implementing the base function trait
145 ///
146 /// Provides composition methods (`and_then`, `when`) for closures
147 /// and function pointers without requiring explicit wrapping.
148 ///
149 /// This trait is automatically implemented for all closures and function
150 /// pointers that implement the base function trait.
151 ///
152 /// # Design Rationale
153 ///
154 /// While closures automatically implement the base function trait through blanket
155 /// implementation, they don't have access to instance methods like `and_then`,
156 /// and `when`. This extension trait provides those methods,
157 /// returning the appropriate Box-based function type for maximum flexibility.
158 ///
159 /// # Examples
160 ///
161 /// ## Chain composition with and_then
162 ///
163 /// ```rust
164 /// use prism3_function::{Function, FnFunctionOps};
165 ///
166 /// let double = |x: i32| x * 2;
167 /// let to_string = |x: i32| x.to_string();
168 ///
169 /// let composed = double.and_then(to_string);
170 /// assert_eq!(composed.apply(21), "42");
171 /// ```
172 ///
173 /// ## Conditional transformation with when
174 ///
175 /// ```rust
176 /// use prism3_function::{Function, FnFunctionOps};
177 ///
178 /// let double = |x: i32| x * 2;
179 /// let conditional = double.when(|x: &i32| *x > 0).or_else(|x: i32| -x);
180 ///
181 /// assert_eq!(conditional.apply(5), 10);
182 /// assert_eq!(conditional.apply(-5), 5);
183 /// ```
184 ///
185 /// # Author
186 ///
187 /// Haixing Hu
188 pub trait $trait_name<T, R>: $($fn_signature)+ + Sized + 'static {
189 /// Chain composition - applies self first, then after
190 ///
191 /// Creates a new function that applies this function first, then
192 /// applies the after function to the result. Consumes self and returns
193 /// a Box-based function.
194 ///
195 /// # Type Parameters
196 ///
197 /// * `S` - The output type of the after function
198 /// * `F` - The type of the after function (must implement the function trait)
199 ///
200 /// # Parameters
201 ///
202 /// * `after` - The function to apply after self. **Note: This parameter
203 /// is passed by value and will transfer ownership.** If you need to
204 /// preserve the original function, clone it first (if it implements
205 /// `Clone`). Can be:
206 /// - A closure
207 /// - A function pointer
208 /// - A Box-based function
209 /// - An Rc-based function
210 /// - An Arc-based function
211 /// - Any type implementing the function trait
212 ///
213 /// # Returns
214 ///
215 /// A new Box-based function representing the composition
216 ///
217 /// # Examples
218 ///
219 /// ## Direct value passing (ownership transfer)
220 ///
221 /// ```rust
222 /// use prism3_function::{Function, FnFunctionOps, BoxFunction};
223 ///
224 /// let double = |x: i32| x * 2;
225 /// let to_string = BoxFunction::new(|x: i32| x.to_string());
226 ///
227 /// // to_string is moved here
228 /// let composed = double.and_then(to_string);
229 /// assert_eq!(composed.apply(21), "42");
230 /// // to_string.apply(5); // Would not compile - moved
231 /// ```
232 ///
233 /// ## Preserving original with clone
234 ///
235 /// ```rust
236 /// use prism3_function::{Function, FnFunctionOps, BoxFunction};
237 ///
238 /// let double = |x: i32| x * 2;
239 /// let to_string = BoxFunction::new(|x: i32| x.to_string());
240 ///
241 /// // Clone to preserve original
242 /// let composed = double.and_then(to_string.clone());
243 /// assert_eq!(composed.apply(21), "42");
244 ///
245 /// // Original still usable
246 /// assert_eq!(to_string.apply(5), "5");
247 /// ```
248 #[allow(unused_mut)]
249 fn and_then<S, F>(mut self, mut after: F) -> $box_type<T, S>
250 where
251 S: 'static,
252 F: $chained_function_trait<R, S> + 'static,
253 T: 'static,
254 R: 'static,
255 {
256 $box_type::new(move |x| {
257 let mut r = self(x);
258 after.apply(&mut r)
259 })
260 }
261
262 /// Creates a conditional function
263 ///
264 /// Returns a function that only executes when a predicate is satisfied.
265 /// You must call `or_else()` to provide an alternative function for when
266 /// the condition is not satisfied.
267 ///
268 /// # Parameters
269 ///
270 /// * `predicate` - The condition to check. **Note: This parameter is passed
271 /// by value and will transfer ownership.** If you need to preserve the
272 /// original predicate, clone it first (if it implements `Clone`). Can be:
273 /// - A closure: `|x: &T| -> bool`
274 /// - A function pointer: `fn(&T) -> bool`
275 /// - A `BoxPredicate<T>`
276 /// - An `RcPredicate<T>`
277 /// - An `ArcPredicate<T>`
278 /// - Any type implementing `Predicate<T>`
279 ///
280 /// # Returns
281 ///
282 /// Returns the appropriate conditional function type
283 ///
284 /// # Examples
285 ///
286 /// ## Basic usage with or_else
287 ///
288 /// ```rust
289 /// use prism3_function::{Function, FnFunctionOps};
290 ///
291 /// let double = |x: i32| x * 2;
292 /// let conditional = double.when(|x: &i32| *x > 0).or_else(|x: i32| -x);
293 ///
294 /// assert_eq!(conditional.apply(5), 10);
295 /// assert_eq!(conditional.apply(-5), 5);
296 /// ```
297 ///
298 /// ## Preserving predicate with clone
299 ///
300 /// ```rust
301 /// use prism3_function::{Function, FnFunctionOps, BoxPredicate};
302 ///
303 /// let double = |x: i32| x * 2;
304 /// let is_positive = BoxPredicate::new(|x: &i32| *x > 0);
305 ///
306 /// // Clone to preserve original predicate
307 /// let conditional = double.when(is_positive.clone())
308 /// .or_else(|x: i32| -x);
309 ///
310 /// assert_eq!(conditional.apply(5), 10);
311 ///
312 /// // Original predicate still usable
313 /// assert!(is_positive.test(&3));
314 /// ```
315 fn when<P>(self, predicate: P) -> $conditional_type<T, R>
316 where
317 P: Predicate<T> + 'static,
318 T: 'static,
319 R: 'static,
320 {
321 $box_type::new(self).when(predicate)
322 }
323 }
324
325 /// Blanket implementation for all closures
326 ///
327 /// Automatically implements the extension trait for any type that
328 /// implements the base function trait.
329 ///
330 /// # Author
331 ///
332 /// Haixing Hu
333 impl<T, R, F> $trait_name<T, R> for F where F: $($fn_signature)+ + 'static {}
334 };
335}
336
337pub(crate) use impl_fn_ops_trait;