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;