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_once(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_once(&5, &3);
123    /// ```
124    fn accept_once(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_once();
147    /// box_consumer.accept_once(&5, &3);
148    /// assert_eq!(*log.lock().unwrap(), vec![8]);
149    /// ```
150    fn into_box_once(self) -> BoxBiConsumerOnce<T, U>
151    where
152        Self: Sized + 'static,
153        T: 'static,
154        U: 'static,
155    {
156        BoxBiConsumerOnce::new(move |t, u| self.accept_once(t, u))
157    }
158
159    /// Converts to a closure
160    ///
161    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
162    /// calling this method.
163    ///
164    /// Converts the one-time bi-consumer to a closure usable with standard
165    /// library methods requiring `FnOnce`.
166    ///
167    /// # Returns
168    ///
169    /// Returns a closure implementing `FnOnce(&T, &U)`
170    fn into_fn_once(self) -> impl FnOnce(&T, &U)
171    where
172        Self: Sized + 'static,
173        T: 'static,
174        U: 'static,
175    {
176        move |t, u| self.accept_once(t, u)
177    }
178
179    /// Convert to BoxBiConsumerOnce without consuming self
180    ///
181    /// **⚠️ Requires Clone**: This method requires `Self` to implement
182    /// `Clone`. Clones the current bi-consumer and then converts the clone
183    /// to a `BoxBiConsumerOnce`.
184    ///
185    /// # Returns
186    ///
187    /// Returns the wrapped `BoxBiConsumerOnce<T, U>`
188    ///
189    /// # Examples
190    ///
191    /// ```rust
192    /// use prism3_function::BiConsumerOnce;
193    /// use std::sync::{Arc, Mutex};
194    ///
195    /// let log = Arc::new(Mutex::new(Vec::new()));
196    /// let l = log.clone();
197    /// let closure = move |x: &i32, y: &i32| {
198    ///     l.lock().unwrap().push(*x + *y);
199    /// };
200    /// let box_consumer = closure.to_box_once();
201    /// box_consumer.accept_once(&5, &3);
202    /// assert_eq!(*log.lock().unwrap(), vec![8]);
203    /// ```
204    fn to_box_once(&self) -> BoxBiConsumerOnce<T, U>
205    where
206        Self: Sized + Clone + 'static,
207        T: 'static,
208        U: 'static,
209    {
210        self.clone().into_box_once()
211    }
212
213    /// Convert to closure without consuming self
214    ///
215    /// **⚠️ Requires Clone**: This method requires `Self` to implement
216    /// `Clone`. Clones the current bi-consumer and then converts the clone
217    /// to a closure.
218    ///
219    /// # Returns
220    ///
221    /// Returns a closure implementing `FnOnce(&T, &U)`
222    ///
223    /// # Examples
224    ///
225    /// ```rust
226    /// use prism3_function::BiConsumerOnce;
227    /// use std::sync::{Arc, Mutex};
228    ///
229    /// let log = Arc::new(Mutex::new(Vec::new()));
230    /// let l = log.clone();
231    /// let closure = move |x: &i32, y: &i32| {
232    ///     l.lock().unwrap().push(*x + *y);
233    /// };
234    /// let func = closure.to_fn_once();
235    /// func(&5, &3);
236    /// assert_eq!(*log.lock().unwrap(), vec![8]);
237    /// ```
238    fn to_fn_once(&self) -> impl FnOnce(&T, &U)
239    where
240        Self: Sized + Clone + 'static,
241        T: 'static,
242        U: 'static,
243    {
244        self.clone().into_fn_once()
245    }
246}
247
248// =======================================================================
249// 2. BoxBiConsumerOnce - Single Ownership Implementation
250// =======================================================================
251
252/// BoxBiConsumerOnce struct
253///
254/// A one-time bi-consumer implementation based on
255/// `Box<dyn FnOnce(&T, &U)>` for single ownership scenarios. This is the
256/// simplest one-time bi-consumer type for truly one-time use.
257///
258/// # Features
259///
260/// - **Single Ownership**: Not cloneable, ownership moves on use
261/// - **Zero Overhead**: No reference counting or locking
262/// - **One-Time Use**: Consumes self on first call
263/// - **Builder Pattern**: Method chaining consumes `self` naturally
264///
265/// # Use Cases
266///
267/// Choose `BoxBiConsumerOnce` when:
268/// - The bi-consumer is truly used only once
269/// - Building pipelines where ownership naturally flows
270/// - The consumer captures values that should be consumed
271/// - Performance is critical and sharing overhead is unacceptable
272///
273/// # Performance
274///
275/// `BoxBiConsumerOnce` has the best performance:
276/// - No reference counting overhead
277/// - No lock acquisition or runtime borrow checking
278/// - Direct function call through vtable
279/// - Minimal memory footprint (single pointer)
280///
281/// # Examples
282///
283/// ```rust
284/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
285///
286/// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
287///     println!("Sum: {}", x + y);
288/// });
289/// consumer.accept_once(&5, &3);
290/// ```
291///
292/// # Author
293///
294/// Haixing Hu
295pub struct BoxBiConsumerOnce<T, U> {
296    function: Box<BiConsumerOnceFn<T, U>>,
297    name: Option<String>,
298}
299
300impl<T, U> BoxBiConsumerOnce<T, U>
301where
302    T: 'static,
303    U: 'static,
304{
305    /// Creates a new BoxBiConsumerOnce
306    ///
307    /// # Type Parameters
308    ///
309    /// * `F` - The closure type
310    ///
311    /// # Parameters
312    ///
313    /// * `f` - The closure to wrap
314    ///
315    /// # Returns
316    ///
317    /// Returns a new `BoxBiConsumerOnce<T, U>` instance
318    ///
319    /// # Examples
320    ///
321    /// ```rust
322    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
323    /// use std::sync::{Arc, Mutex};
324    ///
325    /// let log = Arc::new(Mutex::new(Vec::new()));
326    /// let l = log.clone();
327    /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
328    ///     l.lock().unwrap().push(*x * 2 + *y);
329    /// });
330    /// consumer.accept_once(&5, &3);
331    /// assert_eq!(*log.lock().unwrap(), vec![13]);
332    /// ```
333    pub fn new<F>(f: F) -> Self
334    where
335        F: FnOnce(&T, &U) + 'static,
336    {
337        BoxBiConsumerOnce {
338            function: Box::new(f),
339            name: None,
340        }
341    }
342
343    /// Creates a new BoxBiConsumerOnce with a name
344    ///
345    /// # Type Parameters
346    ///
347    /// * `F` - The closure type
348    ///
349    /// # Parameters
350    ///
351    /// * `name` - The name of the consumer
352    /// * `f` - The closure to wrap
353    ///
354    /// # Returns
355    ///
356    /// Returns a new `BoxBiConsumerOnce<T, U>` instance with the specified name
357    ///
358    /// # Examples
359    ///
360    /// ```rust
361    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
362    /// use std::sync::{Arc, Mutex};
363    ///
364    /// let log = Arc::new(Mutex::new(Vec::new()));
365    /// let l = log.clone();
366    /// let consumer = BoxBiConsumerOnce::new_with_name("sum_logger", move |x: &i32, y: &i32| {
367    ///     l.lock().unwrap().push(*x + *y);
368    /// });
369    /// assert_eq!(consumer.name(), Some("sum_logger"));
370    /// consumer.accept_once(&5, &3);
371    /// assert_eq!(*log.lock().unwrap(), vec![8]);
372    /// ```
373    pub fn new_with_name<F>(name: &str, f: F) -> Self
374    where
375        F: FnOnce(&T, &U) + 'static,
376    {
377        BoxBiConsumerOnce {
378            function: Box::new(f),
379            name: Some(name.to_string()),
380        }
381    }
382
383    /// Gets the name of the consumer
384    pub fn name(&self) -> Option<&str> {
385        self.name.as_deref()
386    }
387
388    /// Sets the name of the consumer
389    pub fn set_name(&mut self, name: impl Into<String>) {
390        self.name = Some(name.into());
391    }
392
393    /// Creates a no-op bi-consumer
394    ///
395    /// # Returns
396    ///
397    /// Returns a no-op bi-consumer
398    ///
399    /// # Examples
400    ///
401    /// ```rust
402    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
403    ///
404    /// let noop = BoxBiConsumerOnce::<i32, i32>::noop();
405    /// noop.accept_once(&42, &10);
406    /// // Values unchanged
407    /// ```
408    pub fn noop() -> Self {
409        BoxBiConsumerOnce::new(|_, _| {})
410    }
411
412    /// Chains another one-time bi-consumer in sequence
413    ///
414    /// Returns a new consumer executing the current operation first, then
415    /// the next operation. Consumes self.
416    ///
417    /// # Type Parameters
418    ///
419    /// * `C` - The type of the next consumer
420    ///
421    /// # Parameters
422    ///
423    /// * `next` - The consumer to execute after the current operation. **Note:
424    ///   This parameter is passed by value and will transfer ownership.** Since
425    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
426    ///   Can be:
427    ///   - A closure: `|x: &T, y: &U|`
428    ///   - A `BoxBiConsumerOnce<T, U>`
429    ///   - Any type implementing `BiConsumerOnce<T, U>`
430    ///
431    /// # Returns
432    ///
433    /// Returns a new composed `BoxBiConsumerOnce<T, U>`
434    ///
435    /// # Examples
436    ///
437    /// ```rust
438    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
439    /// use std::sync::{Arc, Mutex};
440    ///
441    /// let log = Arc::new(Mutex::new(Vec::new()));
442    /// let l1 = log.clone();
443    /// let l2 = log.clone();
444    /// let first = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
445    ///     l1.lock().unwrap().push(*x + *y);
446    /// });
447    /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
448    ///     l2.lock().unwrap().push(*x * *y);
449    /// });
450    ///
451    /// // Both first and second are moved and consumed
452    /// let chained = first.and_then(second);
453    /// chained.accept_once(&5, &3);
454    /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
455    /// // first.accept(&2, &3); // Would not compile - moved
456    /// // second.accept(&2, &3); // Would not compile - moved
457    /// ```
458    pub fn and_then<C>(self, next: C) -> Self
459    where
460        C: BiConsumerOnce<T, U> + 'static,
461    {
462        let first = self.function;
463        let second = next;
464        BoxBiConsumerOnce::new(move |t, u| {
465            first(t, u);
466            second.accept_once(t, u);
467        })
468    }
469
470    /// Creates a conditional bi-consumer
471    ///
472    /// Returns a bi-consumer that only executes when a predicate is satisfied.
473    ///
474    /// # Type Parameters
475    ///
476    /// * `P` - The predicate type
477    ///
478    /// # Parameters
479    ///
480    /// * `predicate` - The condition to check. **Note: This parameter is passed
481    ///   by value and will transfer ownership.** If you need to preserve the
482    ///   original bi-predicate, clone it first (if it implements `Clone`). Can be:
483    ///   - A closure: `|x: &T, y: &U| -> bool`
484    ///   - A function pointer: `fn(&T, &U) -> bool`
485    ///   - A `BoxBiPredicate<T, U>`
486    ///   - Any type implementing `BiPredicate<T, U>`
487    ///
488    /// # Returns
489    ///
490    /// Returns `BoxConditionalBiConsumerOnce<T, U>`
491    ///
492    /// # Examples
493    ///
494    /// ```rust
495    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
496    /// use std::sync::{Arc, Mutex};
497    ///
498    /// let log = Arc::new(Mutex::new(Vec::new()));
499    /// let l = log.clone();
500    /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
501    ///     l.lock().unwrap().push(*x + *y);
502    /// });
503    /// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
504    ///
505    /// conditional.accept_once(&5, &3);
506    /// assert_eq!(*log.lock().unwrap(), vec![8]);
507    /// ```
508    pub fn when<P>(self, predicate: P) -> BoxConditionalBiConsumerOnce<T, U>
509    where
510        P: BiPredicate<T, U> + 'static,
511    {
512        BoxConditionalBiConsumerOnce {
513            consumer: self,
514            predicate: predicate.into_box(),
515        }
516    }
517}
518
519impl<T, U> BiConsumerOnce<T, U> for BoxBiConsumerOnce<T, U> {
520    fn accept_once(self, first: &T, second: &U) {
521        (self.function)(first, second)
522    }
523
524    fn into_box_once(self) -> BoxBiConsumerOnce<T, U>
525    where
526        T: 'static,
527        U: 'static,
528    {
529        self
530    }
531
532    fn into_fn_once(self) -> impl FnOnce(&T, &U)
533    where
534        T: 'static,
535        U: 'static,
536    {
537        self.function
538    }
539}
540
541impl<T, U> fmt::Debug for BoxBiConsumerOnce<T, U> {
542    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543        f.debug_struct("BoxBiConsumerOnce")
544            .field("name", &self.name)
545            .field("function", &"<function>")
546            .finish()
547    }
548}
549
550impl<T, U> fmt::Display for BoxBiConsumerOnce<T, U> {
551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552        match &self.name {
553            Some(name) => write!(f, "BoxBiConsumerOnce({})", name),
554            None => write!(f, "BoxBiConsumerOnce"),
555        }
556    }
557}
558
559// =======================================================================
560// 3. BoxConditionalBiConsumerOnce - Box-based Conditional BiConsumerOnce
561// =======================================================================
562
563/// BoxConditionalBiConsumerOnce struct
564///
565/// A conditional one-time bi-consumer that only executes when a predicate is satisfied.
566/// Uses `BoxBiConsumerOnce` and `BoxBiPredicate` for single ownership semantics.
567///
568/// This type is typically created by calling `BoxBiConsumerOnce::when()` and is
569/// designed to work with the `or_else()` method to create if-then-else logic.
570///
571/// # Features
572///
573/// - **Single Ownership**: Not cloneable, consumes `self` on use
574/// - **Conditional Execution**: Only consumes when predicate returns `true`
575/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
576/// - **Implements BiConsumerOnce**: Can be used anywhere a `BiConsumerOnce` is expected
577///
578/// # Examples
579///
580/// ## Basic Conditional Execution
581///
582/// ```rust
583/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
584/// use std::sync::{Arc, Mutex};
585///
586/// let log = Arc::new(Mutex::new(Vec::new()));
587/// let l = log.clone();
588/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
589///     l.lock().unwrap().push(*x + *y);
590/// });
591/// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
592///
593/// conditional.accept_once(&5, &3);
594/// assert_eq!(*log.lock().unwrap(), vec![8]); // Executed
595/// ```
596///
597/// ## With or_else Branch
598///
599/// ```rust
600/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
601/// use std::sync::{Arc, Mutex};
602///
603/// let log = Arc::new(Mutex::new(Vec::new()));
604/// let l1 = log.clone();
605/// let l2 = log.clone();
606/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
607///     l1.lock().unwrap().push(*x + *y);
608/// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
609///   .or_else(move |x: &i32, y: &i32| {
610///     l2.lock().unwrap().push(*x * *y);
611/// });
612///
613/// consumer.accept_once(&5, &3);
614/// assert_eq!(*log.lock().unwrap(), vec![8]); // when branch executed
615/// ```
616///
617/// # Author
618///
619/// Haixing Hu
620pub struct BoxConditionalBiConsumerOnce<T, U> {
621    consumer: BoxBiConsumerOnce<T, U>,
622    predicate: BoxBiPredicate<T, U>,
623}
624
625impl<T, U> BiConsumerOnce<T, U> for BoxConditionalBiConsumerOnce<T, U>
626where
627    T: 'static,
628    U: 'static,
629{
630    fn accept_once(self, first: &T, second: &U) {
631        if self.predicate.test(first, second) {
632            self.consumer.accept_once(first, second);
633        }
634    }
635
636    fn into_box_once(self) -> BoxBiConsumerOnce<T, U> {
637        let pred = self.predicate;
638        let consumer = self.consumer;
639        BoxBiConsumerOnce::new(move |t, u| {
640            if pred.test(t, u) {
641                consumer.accept_once(t, u);
642            }
643        })
644    }
645
646    fn into_fn_once(self) -> impl FnOnce(&T, &U) {
647        let pred = self.predicate;
648        let consumer = self.consumer;
649        move |t: &T, u: &U| {
650            if pred.test(t, u) {
651                consumer.accept_once(t, u);
652            }
653        }
654    }
655}
656
657impl<T, U> BoxConditionalBiConsumerOnce<T, U>
658where
659    T: 'static,
660    U: 'static,
661{
662    /// Chains another consumer in sequence
663    ///
664    /// Combines the current conditional consumer with another consumer into a new
665    /// consumer. The current conditional consumer executes first, followed by the
666    /// next consumer.
667    ///
668    /// # Parameters
669    ///
670    /// * `next` - The next consumer to execute. **Note: This parameter is passed
671    ///   by value and will transfer ownership.** Since `BoxBiConsumerOnce`
672    ///   cannot be cloned, the parameter will be consumed. Can be:
673    ///   - A closure: `|x: &T, y: &U|`
674    ///   - A `BoxBiConsumerOnce<T, U>`
675    ///   - Any type implementing `BiConsumerOnce<T, U>`
676    ///
677    /// # Returns
678    ///
679    /// Returns a new `BoxBiConsumerOnce<T, U>`
680    ///
681    /// # Examples
682    ///
683    /// ```rust
684    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
685    /// use std::sync::{Arc, Mutex};
686    ///
687    /// let log = Arc::new(Mutex::new(Vec::new()));
688    /// let l1 = log.clone();
689    /// let l2 = log.clone();
690    /// let cond = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
691    ///     l1.lock().unwrap().push(*x + *y);
692    /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0);
693    /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
694    ///     l2.lock().unwrap().push(*x * *y);
695    /// });
696    ///
697    /// // Both cond and second are moved and consumed
698    /// let chained = cond.and_then(second);
699    /// chained.accept_once(&5, &3);
700    /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
701    /// // cond.accept(&2, &3); // Would not compile - moved
702    /// // second.accept(&2, &3); // Would not compile - moved
703    /// ```
704    pub fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
705    where
706        C: BiConsumerOnce<T, U> + 'static,
707    {
708        let first = self;
709        let second = next;
710        BoxBiConsumerOnce::new(move |t, u| {
711            first.accept_once(t, u);
712            second.accept_once(t, u);
713        })
714    }
715
716    /// Adds an else branch
717    ///
718    /// Executes the original consumer when the condition is satisfied, otherwise
719    /// executes else_consumer.
720    ///
721    /// # Parameters
722    ///
723    /// * `else_consumer` - The consumer for the else branch. **Note: This parameter
724    ///   is passed by value and will transfer ownership.** Since
725    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
726    ///   Can be:
727    ///   - A closure: `|x: &T, y: &U|`
728    ///   - A `BoxBiConsumerOnce<T, U>`
729    ///   - Any type implementing `BiConsumerOnce<T, U>`
730    ///
731    /// # Returns
732    ///
733    /// Returns the composed `BoxBiConsumerOnce<T, U>`
734    ///
735    /// # Examples
736    ///
737    /// ## Using a closure (recommended)
738    ///
739    /// ```rust
740    /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
741    /// use std::sync::{Arc, Mutex};
742    ///
743    /// let log = Arc::new(Mutex::new(Vec::new()));
744    /// let l1 = log.clone();
745    /// let l2 = log.clone();
746    /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
747    ///     l1.lock().unwrap().push(*x + *y);
748    /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
749    ///   .or_else(move |x: &i32, y: &i32| {
750    ///     l2.lock().unwrap().push(*x * *y);
751    /// });
752    ///
753    /// consumer.accept_once(&5, &3);
754    /// assert_eq!(*log.lock().unwrap(), vec![8]); // Condition satisfied
755    /// ```
756    pub fn or_else<C>(self, else_consumer: C) -> BoxBiConsumerOnce<T, U>
757    where
758        C: BiConsumerOnce<T, U> + 'static,
759    {
760        let pred = self.predicate;
761        let then_cons = self.consumer;
762        let else_cons = else_consumer;
763        BoxBiConsumerOnce::new(move |t, u| {
764            if pred.test(t, u) {
765                then_cons.accept_once(t, u);
766            } else {
767                else_cons.accept_once(t, u);
768            }
769        })
770    }
771}
772
773// =======================================================================
774// 5. Implement BiConsumerOnce trait for closures
775// =======================================================================
776
777/// Implements BiConsumerOnce for all FnOnce(&T, &U)
778impl<T, U, F> BiConsumerOnce<T, U> for F
779where
780    F: FnOnce(&T, &U),
781{
782    fn accept_once(self, first: &T, second: &U) {
783        self(first, second)
784    }
785
786    fn into_box_once(self) -> BoxBiConsumerOnce<T, U>
787    where
788        Self: Sized + 'static,
789        T: 'static,
790        U: 'static,
791    {
792        BoxBiConsumerOnce::new(self)
793    }
794
795    fn into_fn_once(self) -> impl FnOnce(&T, &U)
796    where
797        Self: Sized + 'static,
798        T: 'static,
799        U: 'static,
800    {
801        self
802    }
803
804    fn to_box_once(&self) -> BoxBiConsumerOnce<T, U>
805    where
806        Self: Sized + Clone + 'static,
807        T: 'static,
808        U: 'static,
809    {
810        BoxBiConsumerOnce::new(self.clone())
811    }
812
813    fn to_fn_once(&self) -> impl FnOnce(&T, &U)
814    where
815        Self: Sized + Clone + 'static,
816        T: 'static,
817        U: 'static,
818    {
819        self.clone()
820    }
821}
822
823// =======================================================================
824// 6. Provide extension methods for closures
825// =======================================================================
826
827/// Extension trait providing one-time bi-consumer composition methods for
828/// closures
829///
830/// Provides `and_then` and other composition methods for all closures
831/// implementing `FnOnce(&T, &U)`, enabling direct method chaining on
832/// closures without explicit wrapper types.
833///
834/// # Features
835///
836/// - **Natural Syntax**: Chain operations directly on closures
837/// - **Returns BoxBiConsumerOnce**: Composition results can be further
838///   chained
839/// - **Zero Cost**: No overhead when composing closures
840/// - **Automatic Implementation**: All `FnOnce(&T, &U)` closures get
841///   these methods automatically
842///
843/// # Examples
844///
845/// ```rust
846/// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
847/// use std::sync::{Arc, Mutex};
848///
849/// let log = Arc::new(Mutex::new(Vec::new()));
850/// let l1 = log.clone();
851/// let l2 = log.clone();
852/// let chained = (move |x: &i32, y: &i32| {
853///     l1.lock().unwrap().push(*x + *y);
854/// }).and_then(move |x: &i32, y: &i32| {
855///     l2.lock().unwrap().push(*x * *y);
856/// });
857/// chained.accept_once(&5, &3);
858/// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
859/// ```
860///
861/// # Author
862///
863/// Haixing Hu
864pub trait FnBiConsumerOnceOps<T, U>: FnOnce(&T, &U) + Sized {
865    /// Chains another one-time bi-consumer in sequence
866    ///
867    /// Returns a new consumer executing the current operation first, then
868    /// the next operation. Consumes the current closure and returns
869    /// `BoxBiConsumerOnce<T, U>`.
870    ///
871    /// # Type Parameters
872    ///
873    /// * `C` - The type of the next consumer
874    ///
875    /// # Parameters
876    ///
877    /// * `next` - The consumer to execute after the current operation. **Note:
878    ///   This parameter is passed by value and will transfer ownership.** Since
879    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
880    ///   Can be:
881    ///   - A closure: `|x: &T, y: &U|`
882    ///   - A `BoxBiConsumerOnce<T, U>`
883    ///   - Any type implementing `BiConsumerOnce<T, U>`
884    ///
885    /// # Returns
886    ///
887    /// Returns the composed `BoxBiConsumerOnce<T, U>`
888    ///
889    /// # Examples
890    ///
891    /// ```rust
892    /// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
893    /// use std::sync::{Arc, Mutex};
894    ///
895    /// let log = Arc::new(Mutex::new(Vec::new()));
896    /// let l1 = log.clone();
897    /// let l2 = log.clone();
898    /// let chained = (move |x: &i32, y: &i32| {
899    ///     l1.lock().unwrap().push(*x + *y);
900    /// }).and_then(move |x: &i32, y: &i32| {
901    ///     l2.lock().unwrap().push(*x * *y);
902    /// }).and_then(|x: &i32, y: &i32| {
903    ///     println!("Result: {}, {}", x, y);
904    /// });
905    ///
906    /// chained.accept_once(&5, &3);
907    /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
908    /// ```
909    fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
910    where
911        Self: 'static,
912        C: BiConsumerOnce<T, U> + 'static,
913        T: 'static,
914        U: 'static,
915    {
916        let first = self;
917        let second = next;
918        BoxBiConsumerOnce::new(move |t, u| {
919            first(t, u);
920            second.accept_once(t, u);
921        })
922    }
923}
924
925/// Implements FnBiConsumerOnceOps for all closure types
926impl<T, U, F> FnBiConsumerOnceOps<T, U> for F where F: FnOnce(&T, &U) {}