prism3_function/
bi_consumer_once.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025.
4 *    3-Prism Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # BiConsumerOnce Types
10//!
11//! Provides one-time bi-consumer interface implementations for operations
12//! accepting two input parameters without returning a result.
13//!
14//! This module provides a unified `BiConsumerOnce` trait and one concrete
15//! implementation:
16//!
17//! - **`BoxBiConsumerOnce<T, U>`**: Box-based single ownership
18//!   implementation
19//!
20//! # Why No Arc/Rc Variants?
21//!
22//! Unlike `BiConsumer` and `ReadonlyBiConsumer`, this module does **not**
23//! provide `ArcBiConsumerOnce` or `RcBiConsumerOnce` implementations. This
24//! is a design decision based on the fundamental incompatibility between
25//! `FnOnce` semantics and shared ownership. See the design documentation
26//! for details.
27//!
28//! # Design Philosophy
29//!
30//! BiConsumerOnce uses `FnOnce(&T, &U)` semantics: for truly one-time
31//! consumption operations. Unlike BiConsumer, BiConsumerOnce consumes
32//! itself on first call. Suitable for initialization callbacks, cleanup
33//! callbacks, etc.
34//!
35//! # Author
36//!
37//! Haixing Hu
38
39use std::fmt;
40
41use crate::bi_predicate::{BiPredicate, BoxBiPredicate};
42
43// ==========================================================================
44// Type Aliases
45// ==========================================================================
46
47/// Type alias for bi-consumer once function signature.
48type BiConsumerOnceFn<T, U> = dyn FnOnce(&T, &U);
49
50// =======================================================================
51// 1. BiConsumerOnce Trait - Unified Interface
52// =======================================================================
53
54/// BiConsumerOnce trait - Unified one-time bi-consumer interface
55///
56/// Defines core behavior for all one-time bi-consumer types. Similar to a
57/// bi-consumer implementing `FnOnce(&T, &U)`, performs operations
58/// accepting two value references but returning no result (side effects
59/// only), consuming itself in the process.
60///
61/// # Automatic Implementations
62///
63/// - All closures implementing `FnOnce(&T, &U)`
64/// - `BoxBiConsumerOnce<T, U>`
65///
66/// # Features
67///
68/// - **Unified Interface**: All bi-consumer types share the same `accept`
69///   method signature
70/// - **Automatic Implementation**: Closures automatically implement this
71///   trait with zero overhead
72/// - **Type Conversions**: Can convert to BoxBiConsumerOnce
73/// - **Generic Programming**: Write functions accepting any one-time
74///   bi-consumer type
75///
76/// # Examples
77///
78/// ```rust
79/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
80/// use std::sync::{Arc, Mutex};
81///
82/// fn apply_consumer<C: BiConsumerOnce<i32, i32>>(
83///     consumer: C,
84///     a: &i32,
85///     b: &i32
86/// ) {
87///     consumer.accept(a, b);
88/// }
89///
90/// let log = Arc::new(Mutex::new(Vec::new()));
91/// let l = log.clone();
92/// let box_con = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
93///     l.lock().unwrap().push(*x + *y);
94/// });
95/// apply_consumer(box_con, &5, &3);
96/// assert_eq!(*log.lock().unwrap(), vec![8]);
97/// ```
98///
99/// # Author
100///
101/// Haixing Hu
102pub trait BiConsumerOnce<T, U> {
103    /// Performs the one-time consumption operation
104    ///
105    /// Executes an operation on the given two references. The operation
106    /// typically reads input values or produces side effects, but does not
107    /// modify the input values themselves. Consumes self.
108    ///
109    /// # Parameters
110    ///
111    /// * `first` - Reference to the first value to consume
112    /// * `second` - Reference to the second value to consume
113    ///
114    /// # Examples
115    ///
116    /// ```rust
117    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
118    ///
119    /// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
120    ///     println!("Sum: {}", x + y);
121    /// });
122    /// consumer.accept(&5, &3);
123    /// ```
124    fn accept(self, first: &T, second: &U);
125
126    /// Converts to BoxBiConsumerOnce
127    ///
128    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
129    /// calling this method.
130    ///
131    /// # Returns
132    ///
133    /// Returns the wrapped `BoxBiConsumerOnce<T, U>`
134    ///
135    /// # Examples
136    ///
137    /// ```rust
138    /// use prism3_function::BiConsumerOnce;
139    /// use std::sync::{Arc, Mutex};
140    ///
141    /// let log = Arc::new(Mutex::new(Vec::new()));
142    /// let l = log.clone();
143    /// let closure = move |x: &i32, y: &i32| {
144    ///     l.lock().unwrap().push(*x + *y);
145    /// };
146    /// let box_consumer = closure.into_box();
147    /// box_consumer.accept(&5, &3);
148    /// assert_eq!(*log.lock().unwrap(), vec![8]);
149    /// ```
150    fn into_box(self) -> BoxBiConsumerOnce<T, U>
151    where
152        Self: Sized + 'static,
153        T: 'static,
154        U: 'static;
155
156    /// Converts to a closure
157    ///
158    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
159    /// calling this method.
160    ///
161    /// Converts the one-time bi-consumer to a closure usable with standard
162    /// library methods requiring `FnOnce`.
163    ///
164    /// # Returns
165    ///
166    /// Returns a closure implementing `FnOnce(&T, &U)`
167    fn into_fn(self) -> impl FnOnce(&T, &U)
168    where
169        Self: Sized + 'static,
170        T: 'static,
171        U: 'static;
172}
173
174// =======================================================================
175// 2. BoxBiConsumerOnce - Single Ownership Implementation
176// =======================================================================
177
178/// BoxBiConsumerOnce struct
179///
180/// A one-time bi-consumer implementation based on
181/// `Box<dyn FnOnce(&T, &U)>` for single ownership scenarios. This is the
182/// simplest one-time bi-consumer type for truly one-time use.
183///
184/// # Features
185///
186/// - **Single Ownership**: Not cloneable, ownership moves on use
187/// - **Zero Overhead**: No reference counting or locking
188/// - **One-Time Use**: Consumes self on first call
189/// - **Builder Pattern**: Method chaining consumes `self` naturally
190///
191/// # Use Cases
192///
193/// Choose `BoxBiConsumerOnce` when:
194/// - The bi-consumer is truly used only once
195/// - Building pipelines where ownership naturally flows
196/// - The consumer captures values that should be consumed
197/// - Performance is critical and sharing overhead is unacceptable
198///
199/// # Performance
200///
201/// `BoxBiConsumerOnce` has the best performance:
202/// - No reference counting overhead
203/// - No lock acquisition or runtime borrow checking
204/// - Direct function call through vtable
205/// - Minimal memory footprint (single pointer)
206///
207/// # Examples
208///
209/// ```rust
210/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
211///
212/// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
213///     println!("Sum: {}", x + y);
214/// });
215/// consumer.accept(&5, &3);
216/// ```
217///
218/// # Author
219///
220/// Haixing Hu
221pub struct BoxBiConsumerOnce<T, U> {
222    function: Box<BiConsumerOnceFn<T, U>>,
223    name: Option<String>,
224}
225
226impl<T, U> BoxBiConsumerOnce<T, U>
227where
228    T: 'static,
229    U: 'static,
230{
231    /// Creates a new BoxBiConsumerOnce
232    ///
233    /// # Type Parameters
234    ///
235    /// * `F` - The closure type
236    ///
237    /// # Parameters
238    ///
239    /// * `f` - The closure to wrap
240    ///
241    /// # Returns
242    ///
243    /// Returns a new `BoxBiConsumerOnce<T, U>` instance
244    ///
245    /// # Examples
246    ///
247    /// ```rust
248    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
249    /// use std::sync::{Arc, Mutex};
250    ///
251    /// let log = Arc::new(Mutex::new(Vec::new()));
252    /// let l = log.clone();
253    /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
254    ///     l.lock().unwrap().push(*x * 2 + *y);
255    /// });
256    /// consumer.accept(&5, &3);
257    /// assert_eq!(*log.lock().unwrap(), vec![13]);
258    /// ```
259    pub fn new<F>(f: F) -> Self
260    where
261        F: FnOnce(&T, &U) + 'static,
262    {
263        BoxBiConsumerOnce {
264            function: Box::new(f),
265            name: None,
266        }
267    }
268
269    /// Creates a new BoxBiConsumerOnce with a name
270    ///
271    /// # Type Parameters
272    ///
273    /// * `F` - The closure type
274    ///
275    /// # Parameters
276    ///
277    /// * `name` - The name of the consumer
278    /// * `f` - The closure to wrap
279    ///
280    /// # Returns
281    ///
282    /// Returns a new `BoxBiConsumerOnce<T, U>` instance with the specified name
283    ///
284    /// # Examples
285    ///
286    /// ```rust
287    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
288    /// use std::sync::{Arc, Mutex};
289    ///
290    /// let log = Arc::new(Mutex::new(Vec::new()));
291    /// let l = log.clone();
292    /// let consumer = BoxBiConsumerOnce::new_with_name("sum_logger", move |x: &i32, y: &i32| {
293    ///     l.lock().unwrap().push(*x + *y);
294    /// });
295    /// assert_eq!(consumer.name(), Some("sum_logger"));
296    /// consumer.accept(&5, &3);
297    /// assert_eq!(*log.lock().unwrap(), vec![8]);
298    /// ```
299    pub fn new_with_name<F>(name: &str, f: F) -> Self
300    where
301        F: FnOnce(&T, &U) + 'static,
302    {
303        BoxBiConsumerOnce {
304            function: Box::new(f),
305            name: Some(name.to_string()),
306        }
307    }
308
309    /// Gets the name of the consumer
310    pub fn name(&self) -> Option<&str> {
311        self.name.as_deref()
312    }
313
314    /// Sets the name of the consumer
315    pub fn set_name(&mut self, name: impl Into<String>) {
316        self.name = Some(name.into());
317    }
318
319    /// Creates a no-op bi-consumer
320    ///
321    /// # Returns
322    ///
323    /// Returns a no-op bi-consumer
324    ///
325    /// # Examples
326    ///
327    /// ```rust
328    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
329    ///
330    /// let noop = BoxBiConsumerOnce::<i32, i32>::noop();
331    /// noop.accept(&42, &10);
332    /// // Values unchanged
333    /// ```
334    pub fn noop() -> Self {
335        BoxBiConsumerOnce::new(|_, _| {})
336    }
337
338    /// Chains another one-time bi-consumer in sequence
339    ///
340    /// Returns a new consumer executing the current operation first, then
341    /// the next operation. Consumes self.
342    ///
343    /// # Type Parameters
344    ///
345    /// * `C` - The type of the next consumer
346    ///
347    /// # Parameters
348    ///
349    /// * `next` - The consumer to execute after the current operation. **Note:
350    ///   This parameter is passed by value and will transfer ownership.** Since
351    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
352    ///   Can be:
353    ///   - A closure: `|x: &T, y: &U|`
354    ///   - A `BoxBiConsumerOnce<T, U>`
355    ///   - Any type implementing `BiConsumerOnce<T, U>`
356    ///
357    /// # Returns
358    ///
359    /// Returns a new composed `BoxBiConsumerOnce<T, U>`
360    ///
361    /// # Examples
362    ///
363    /// ```rust
364    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
365    /// use std::sync::{Arc, Mutex};
366    ///
367    /// let log = Arc::new(Mutex::new(Vec::new()));
368    /// let l1 = log.clone();
369    /// let l2 = log.clone();
370    /// let first = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
371    ///     l1.lock().unwrap().push(*x + *y);
372    /// });
373    /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
374    ///     l2.lock().unwrap().push(*x * *y);
375    /// });
376    ///
377    /// // Both first and second are moved and consumed
378    /// let chained = first.and_then(second);
379    /// chained.accept(&5, &3);
380    /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
381    /// // first.accept(&2, &3); // Would not compile - moved
382    /// // second.accept(&2, &3); // Would not compile - moved
383    /// ```
384    pub fn and_then<C>(self, next: C) -> Self
385    where
386        C: BiConsumerOnce<T, U> + 'static,
387    {
388        let first = self.function;
389        let second = next;
390        BoxBiConsumerOnce::new(move |t, u| {
391            first(t, u);
392            second.accept(t, u);
393        })
394    }
395
396    /// Creates a conditional bi-consumer
397    ///
398    /// Returns a bi-consumer that only executes when a predicate is satisfied.
399    ///
400    /// # Type Parameters
401    ///
402    /// * `P` - The predicate type
403    ///
404    /// # Parameters
405    ///
406    /// * `predicate` - The condition to check. **Note: This parameter is passed
407    ///   by value and will transfer ownership.** If you need to preserve the
408    ///   original bi-predicate, clone it first (if it implements `Clone`). Can be:
409    ///   - A closure: `|x: &T, y: &U| -> bool`
410    ///   - A function pointer: `fn(&T, &U) -> bool`
411    ///   - A `BoxBiPredicate<T, U>`
412    ///   - Any type implementing `BiPredicate<T, U>`
413    ///
414    /// # Returns
415    ///
416    /// Returns `BoxConditionalBiConsumerOnce<T, U>`
417    ///
418    /// # Examples
419    ///
420    /// ```rust
421    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
422    /// use std::sync::{Arc, Mutex};
423    ///
424    /// let log = Arc::new(Mutex::new(Vec::new()));
425    /// let l = log.clone();
426    /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
427    ///     l.lock().unwrap().push(*x + *y);
428    /// });
429    /// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
430    ///
431    /// conditional.accept(&5, &3);
432    /// assert_eq!(*log.lock().unwrap(), vec![8]);
433    /// ```
434    pub fn when<P>(self, predicate: P) -> BoxConditionalBiConsumerOnce<T, U>
435    where
436        P: BiPredicate<T, U> + 'static,
437    {
438        BoxConditionalBiConsumerOnce {
439            consumer: self,
440            predicate: predicate.into_box(),
441        }
442    }
443}
444
445impl<T, U> BiConsumerOnce<T, U> for BoxBiConsumerOnce<T, U> {
446    fn accept(self, first: &T, second: &U) {
447        (self.function)(first, second)
448    }
449
450    fn into_box(self) -> BoxBiConsumerOnce<T, U>
451    where
452        T: 'static,
453        U: 'static,
454    {
455        self
456    }
457
458    fn into_fn(self) -> impl FnOnce(&T, &U)
459    where
460        T: 'static,
461        U: 'static,
462    {
463        self.function
464    }
465}
466
467impl<T, U> fmt::Debug for BoxBiConsumerOnce<T, U> {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        f.debug_struct("BoxBiConsumerOnce")
470            .field("name", &self.name)
471            .field("function", &"<function>")
472            .finish()
473    }
474}
475
476impl<T, U> fmt::Display for BoxBiConsumerOnce<T, U> {
477    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478        match &self.name {
479            Some(name) => write!(f, "BoxBiConsumerOnce({})", name),
480            None => write!(f, "BoxBiConsumerOnce"),
481        }
482    }
483}
484
485// =======================================================================
486// 3. BoxConditionalBiConsumerOnce - Box-based Conditional BiConsumerOnce
487// =======================================================================
488
489/// BoxConditionalBiConsumerOnce struct
490///
491/// A conditional one-time bi-consumer that only executes when a predicate is satisfied.
492/// Uses `BoxBiConsumerOnce` and `BoxBiPredicate` for single ownership semantics.
493///
494/// This type is typically created by calling `BoxBiConsumerOnce::when()` and is
495/// designed to work with the `or_else()` method to create if-then-else logic.
496///
497/// # Features
498///
499/// - **Single Ownership**: Not cloneable, consumes `self` on use
500/// - **Conditional Execution**: Only consumes when predicate returns `true`
501/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
502/// - **Implements BiConsumerOnce**: Can be used anywhere a `BiConsumerOnce` is expected
503///
504/// # Examples
505///
506/// ## Basic Conditional Execution
507///
508/// ```rust
509/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
510/// use std::sync::{Arc, Mutex};
511///
512/// let log = Arc::new(Mutex::new(Vec::new()));
513/// let l = log.clone();
514/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
515///     l.lock().unwrap().push(*x + *y);
516/// });
517/// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
518///
519/// conditional.accept(&5, &3);
520/// assert_eq!(*log.lock().unwrap(), vec![8]); // Executed
521/// ```
522///
523/// ## With or_else Branch
524///
525/// ```rust
526/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
527/// use std::sync::{Arc, Mutex};
528///
529/// let log = Arc::new(Mutex::new(Vec::new()));
530/// let l1 = log.clone();
531/// let l2 = log.clone();
532/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
533///     l1.lock().unwrap().push(*x + *y);
534/// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
535///   .or_else(move |x: &i32, y: &i32| {
536///     l2.lock().unwrap().push(*x * *y);
537/// });
538///
539/// consumer.accept(&5, &3);
540/// assert_eq!(*log.lock().unwrap(), vec![8]); // when branch executed
541/// ```
542///
543/// # Author
544///
545/// Haixing Hu
546pub struct BoxConditionalBiConsumerOnce<T, U> {
547    consumer: BoxBiConsumerOnce<T, U>,
548    predicate: BoxBiPredicate<T, U>,
549}
550
551impl<T, U> BiConsumerOnce<T, U> for BoxConditionalBiConsumerOnce<T, U>
552where
553    T: 'static,
554    U: 'static,
555{
556    fn accept(self, first: &T, second: &U) {
557        if self.predicate.test(first, second) {
558            self.consumer.accept(first, second);
559        }
560    }
561
562    fn into_box(self) -> BoxBiConsumerOnce<T, U> {
563        let pred = self.predicate;
564        let consumer = self.consumer;
565        BoxBiConsumerOnce::new(move |t, u| {
566            if pred.test(t, u) {
567                consumer.accept(t, u);
568            }
569        })
570    }
571
572    fn into_fn(self) -> impl FnOnce(&T, &U) {
573        let pred = self.predicate;
574        let consumer = self.consumer;
575        move |t: &T, u: &U| {
576            if pred.test(t, u) {
577                consumer.accept(t, u);
578            }
579        }
580    }
581}
582
583impl<T, U> BoxConditionalBiConsumerOnce<T, U>
584where
585    T: 'static,
586    U: 'static,
587{
588    /// Chains another consumer in sequence
589    ///
590    /// Combines the current conditional consumer with another consumer into a new
591    /// consumer. The current conditional consumer executes first, followed by the
592    /// next consumer.
593    ///
594    /// # Parameters
595    ///
596    /// * `next` - The next consumer to execute. **Note: This parameter is passed
597    ///   by value and will transfer ownership.** Since `BoxBiConsumerOnce`
598    ///   cannot be cloned, the parameter will be consumed. Can be:
599    ///   - A closure: `|x: &T, y: &U|`
600    ///   - A `BoxBiConsumerOnce<T, U>`
601    ///   - Any type implementing `BiConsumerOnce<T, U>`
602    ///
603    /// # Returns
604    ///
605    /// Returns a new `BoxBiConsumerOnce<T, U>`
606    ///
607    /// # Examples
608    ///
609    /// ```rust
610    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
611    /// use std::sync::{Arc, Mutex};
612    ///
613    /// let log = Arc::new(Mutex::new(Vec::new()));
614    /// let l1 = log.clone();
615    /// let l2 = log.clone();
616    /// let cond = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
617    ///     l1.lock().unwrap().push(*x + *y);
618    /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0);
619    /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
620    ///     l2.lock().unwrap().push(*x * *y);
621    /// });
622    ///
623    /// // Both cond and second are moved and consumed
624    /// let chained = cond.and_then(second);
625    /// chained.accept(&5, &3);
626    /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
627    /// // cond.accept(&2, &3); // Would not compile - moved
628    /// // second.accept(&2, &3); // Would not compile - moved
629    /// ```
630    pub fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
631    where
632        C: BiConsumerOnce<T, U> + 'static,
633    {
634        let first = self;
635        let second = next;
636        BoxBiConsumerOnce::new(move |t, u| {
637            first.accept(t, u);
638            second.accept(t, u);
639        })
640    }
641
642    /// Adds an else branch
643    ///
644    /// Executes the original consumer when the condition is satisfied, otherwise
645    /// executes else_consumer.
646    ///
647    /// # Parameters
648    ///
649    /// * `else_consumer` - The consumer for the else branch. **Note: This parameter
650    ///   is passed by value and will transfer ownership.** Since
651    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
652    ///   Can be:
653    ///   - A closure: `|x: &T, y: &U|`
654    ///   - A `BoxBiConsumerOnce<T, U>`
655    ///   - Any type implementing `BiConsumerOnce<T, U>`
656    ///
657    /// # Returns
658    ///
659    /// Returns the composed `BoxBiConsumerOnce<T, U>`
660    ///
661    /// # Examples
662    ///
663    /// ## Using a closure (recommended)
664    ///
665    /// ```rust
666    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
667    /// use std::sync::{Arc, Mutex};
668    ///
669    /// let log = Arc::new(Mutex::new(Vec::new()));
670    /// let l1 = log.clone();
671    /// let l2 = log.clone();
672    /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
673    ///     l1.lock().unwrap().push(*x + *y);
674    /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
675    ///   .or_else(move |x: &i32, y: &i32| {
676    ///     l2.lock().unwrap().push(*x * *y);
677    /// });
678    ///
679    /// consumer.accept(&5, &3);
680    /// assert_eq!(*log.lock().unwrap(), vec![8]); // Condition satisfied
681    /// ```
682    pub fn or_else<C>(self, else_consumer: C) -> BoxBiConsumerOnce<T, U>
683    where
684        C: BiConsumerOnce<T, U> + 'static,
685    {
686        let pred = self.predicate;
687        let then_cons = self.consumer;
688        let else_cons = else_consumer;
689        BoxBiConsumerOnce::new(move |t, u| {
690            if pred.test(t, u) {
691                then_cons.accept(t, u);
692            } else {
693                else_cons.accept(t, u);
694            }
695        })
696    }
697}
698
699// =======================================================================
700// 5. Implement BiConsumerOnce trait for closures
701// =======================================================================
702
703/// Implements BiConsumerOnce for all FnOnce(&T, &U)
704impl<T, U, F> BiConsumerOnce<T, U> for F
705where
706    F: FnOnce(&T, &U),
707{
708    fn accept(self, first: &T, second: &U) {
709        self(first, second)
710    }
711
712    fn into_box(self) -> BoxBiConsumerOnce<T, U>
713    where
714        Self: Sized + 'static,
715        T: 'static,
716        U: 'static,
717    {
718        BoxBiConsumerOnce::new(self)
719    }
720
721    fn into_fn(self) -> impl FnOnce(&T, &U)
722    where
723        Self: Sized + 'static,
724        T: 'static,
725        U: 'static,
726    {
727        self
728    }
729}
730
731// =======================================================================
732// 6. Provide extension methods for closures
733// =======================================================================
734
735/// Extension trait providing one-time bi-consumer composition methods for
736/// closures
737///
738/// Provides `and_then` and other composition methods for all closures
739/// implementing `FnOnce(&T, &U)`, enabling direct method chaining on
740/// closures without explicit wrapper types.
741///
742/// # Features
743///
744/// - **Natural Syntax**: Chain operations directly on closures
745/// - **Returns BoxBiConsumerOnce**: Composition results can be further
746///   chained
747/// - **Zero Cost**: No overhead when composing closures
748/// - **Automatic Implementation**: All `FnOnce(&T, &U)` closures get
749///   these methods automatically
750///
751/// # Examples
752///
753/// ```rust
754/// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
755/// use std::sync::{Arc, Mutex};
756///
757/// let log = Arc::new(Mutex::new(Vec::new()));
758/// let l1 = log.clone();
759/// let l2 = log.clone();
760/// let chained = (move |x: &i32, y: &i32| {
761///     l1.lock().unwrap().push(*x + *y);
762/// }).and_then(move |x: &i32, y: &i32| {
763///     l2.lock().unwrap().push(*x * *y);
764/// });
765/// chained.accept(&5, &3);
766/// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
767/// ```
768///
769/// # Author
770///
771/// Haixing Hu
772pub trait FnBiConsumerOnceOps<T, U>: FnOnce(&T, &U) + Sized {
773    /// Chains another one-time bi-consumer in sequence
774    ///
775    /// Returns a new consumer executing the current operation first, then
776    /// the next operation. Consumes the current closure and returns
777    /// `BoxBiConsumerOnce<T, U>`.
778    ///
779    /// # Type Parameters
780    ///
781    /// * `C` - The type of the next consumer
782    ///
783    /// # Parameters
784    ///
785    /// * `next` - The consumer to execute after the current operation. **Note:
786    ///   This parameter is passed by value and will transfer ownership.** Since
787    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
788    ///   Can be:
789    ///   - A closure: `|x: &T, y: &U|`
790    ///   - A `BoxBiConsumerOnce<T, U>`
791    ///   - Any type implementing `BiConsumerOnce<T, U>`
792    ///
793    /// # Returns
794    ///
795    /// Returns the composed `BoxBiConsumerOnce<T, U>`
796    ///
797    /// # Examples
798    ///
799    /// ```rust
800    /// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
801    /// use std::sync::{Arc, Mutex};
802    ///
803    /// let log = Arc::new(Mutex::new(Vec::new()));
804    /// let l1 = log.clone();
805    /// let l2 = log.clone();
806    /// let chained = (move |x: &i32, y: &i32| {
807    ///     l1.lock().unwrap().push(*x + *y);
808    /// }).and_then(move |x: &i32, y: &i32| {
809    ///     l2.lock().unwrap().push(*x * *y);
810    /// }).and_then(|x: &i32, y: &i32| {
811    ///     println!("Result: {}, {}", x, y);
812    /// });
813    ///
814    /// chained.accept(&5, &3);
815    /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
816    /// ```
817    fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
818    where
819        Self: 'static,
820        C: BiConsumerOnce<T, U> + 'static,
821        T: 'static,
822        U: 'static,
823    {
824        let first = self;
825        let second = next;
826        BoxBiConsumerOnce::new(move |t, u| {
827            first(t, u);
828            second.accept(t, u);
829        })
830    }
831}
832
833/// Implements FnBiConsumerOnceOps for all closure types
834impl<T, U, F> FnBiConsumerOnceOps<T, U> for F where F: FnOnce(&T, &U) {}