Skip to main content

qubit_function/consumers/
bi_consumer.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # BiConsumer Types
10//!
11//! Provides readonly bi-consumer interface implementations for operations
12//! that accept two input parameters without modifying their own state or
13//! the input values.
14//!
15//! It is similar to the `Fn(&T, &U)` trait in the standard library.
16//!
17//! This module provides a unified `BiConsumer` trait and three
18//! concrete implementations based on different ownership models:
19//!
20//! - **`BoxBiConsumer<T, U>`**: Box-based single ownership
21//! - **`ArcBiConsumer<T, U>`**: Arc-based thread-safe shared
22//!   ownership
23//! - **`RcBiConsumer<T, U>`**: Rc-based single-threaded shared
24//!   ownership
25//!
26//! # Design Philosophy
27//!
28//! BiConsumer uses `Fn(&T, &U)` semantics: neither modifies its
29//! own state nor the input values.
30//!
31//! Suitable for pure observation, logging, and notification scenarios with two
32//! parameters. Compared to BiConsumer, BiConsumer does not require interior
33//! mutability (Mutex/RefCell), thus more efficient and easier to share.
34//!
35//! # Author
36//!
37//! Haixing Hu
38use std::rc::Rc;
39use std::sync::Arc;
40
41use crate::consumers::{
42    bi_consumer_once::BoxBiConsumerOnce,
43    macros::{
44        impl_box_conditional_consumer,
45        impl_box_consumer_methods,
46        impl_conditional_consumer_clone,
47        impl_conditional_consumer_conversions,
48        impl_conditional_consumer_debug_display,
49        impl_consumer_clone,
50        impl_consumer_common_methods,
51        impl_consumer_debug_display,
52        impl_shared_conditional_consumer,
53        impl_shared_consumer_methods,
54    },
55};
56use crate::macros::{
57    impl_arc_conversions,
58    impl_box_conversions,
59    impl_closure_trait,
60    impl_rc_conversions,
61};
62use crate::predicates::bi_predicate::{
63    ArcBiPredicate,
64    BiPredicate,
65    BoxBiPredicate,
66    RcBiPredicate,
67};
68
69// ==========================================================================
70// Type Aliases
71// ==========================================================================
72
73/// Type alias for readonly bi-consumer function signature.
74type BiConsumerFn<T, U> = dyn Fn(&T, &U);
75
76/// Type alias for thread-safe readonly bi-consumer function signature.
77type ThreadSafeBiConsumerFn<T, U> = dyn Fn(&T, &U) + Send + Sync;
78
79// =======================================================================
80// 1. BiConsumer Trait - Unified Interface
81// =======================================================================
82
83/// BiConsumer trait - Unified readonly bi-consumer interface
84///
85/// It is similar to the `Fn(&T, &U)` trait in the standard library.
86///
87/// Defines core behavior for all readonly bi-consumer types. Unlike
88/// `BiConsumer`, `BiConsumer` neither modifies its own state nor
89/// the input values, making it a fully immutable operation.
90///
91/// # Automatic Implementations
92///
93/// - All closures implementing `Fn(&T, &U)`
94/// - `BoxBiConsumer<T, U>`, `ArcBiConsumer<T, U>`,
95///   `RcBiConsumer<T, U>`
96///
97/// # Features
98///
99/// - **Unified Interface**: All readonly bi-consumer types share the same
100///   `accept` method signature
101/// - **Automatic Implementation**: Closures automatically implement this
102///   trait with zero overhead
103/// - **Type Conversions**: Easy conversion between ownership models
104/// - **Generic Programming**: Write functions accepting any readonly
105///   bi-consumer type
106/// - **No Interior Mutability**: No need for Mutex or RefCell, more
107///   efficient
108///
109/// # Examples
110///
111/// ```rust
112/// use qubit_function::{BiConsumer, BoxBiConsumer};
113///
114/// fn apply_consumer<C: BiConsumer<i32, i32>>(
115///     consumer: &C,
116///     a: &i32,
117///     b: &i32
118/// ) {
119///     consumer.accept(a, b);
120/// }
121///
122/// let box_con = BoxBiConsumer::new(|x: &i32, y: &i32| {
123///     println!("Sum: {}", x + y);
124/// });
125/// apply_consumer(&box_con, &5, &3);
126/// ```
127///
128/// # Author
129///
130/// Haixing Hu
131pub trait BiConsumer<T, U> {
132    /// Performs the readonly consumption operation
133    ///
134    /// Executes an operation on the given two references. The operation
135    /// typically reads input values or produces side effects, but neither
136    /// modifies the input values nor the consumer's own state.
137    ///
138    /// # Parameters
139    ///
140    /// * `first` - Reference to the first value to consume
141    /// * `second` - Reference to the second value to consume
142    ///
143    /// # Examples
144    ///
145    /// ```rust
146    /// use qubit_function::{BiConsumer, BoxBiConsumer};
147    ///
148    /// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
149    ///     println!("Values: {}, {}", x, y);
150    /// });
151    /// consumer.accept(&5, &3);
152    /// ```
153    fn accept(&self, first: &T, second: &U);
154
155    /// Converts to BoxBiConsumer
156    ///
157    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
158    /// calling this method.
159    ///
160    /// # Returns
161    ///
162    /// Returns the wrapped `BoxBiConsumer<T, U>`
163    fn into_box(self) -> BoxBiConsumer<T, U>
164    where
165        Self: Sized + 'static,
166    {
167        BoxBiConsumer::new(move |t, u| self.accept(t, u))
168    }
169
170    /// Converts to RcBiConsumer
171    ///
172    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
173    /// calling this method.
174    ///
175    /// # Returns
176    ///
177    /// Returns the wrapped `RcBiConsumer<T, U>`
178    fn into_rc(self) -> RcBiConsumer<T, U>
179    where
180        Self: Sized + 'static,
181    {
182        RcBiConsumer::new(move |t, u| self.accept(t, u))
183    }
184
185    /// Converts to ArcBiConsumer
186    ///
187    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
188    /// calling this method.
189    ///
190    /// # Returns
191    ///
192    /// Returns the wrapped `ArcBiConsumer<T, U>`
193    fn into_arc(self) -> ArcBiConsumer<T, U>
194    where
195        Self: Sized + Send + Sync + 'static,
196    {
197        ArcBiConsumer::new(move |t, u| self.accept(t, u))
198    }
199
200    /// Converts readonly bi-consumer to a closure
201    ///
202    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
203    /// calling this method.
204    ///
205    /// Converts the readonly bi-consumer to a closure usable with standard
206    /// library methods requiring `Fn`.
207    ///
208    /// # Returns
209    ///
210    /// Returns a closure implementing `Fn(&T, &U)`
211    ///
212    /// # Examples
213    ///
214    /// ```rust
215    /// use qubit_function::{BiConsumer, BoxBiConsumer};
216    ///
217    /// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
218    ///     println!("Sum: {}", x + y);
219    /// });
220    /// let func = consumer.into_fn();
221    /// func(&5, &3);
222    /// ```
223    fn into_fn(self) -> impl Fn(&T, &U)
224    where
225        Self: Sized + 'static,
226    {
227        move |t, u| self.accept(t, u)
228    }
229
230    /// Convert to BiConsumerOnce
231    ///
232    /// **⚠️ Consumes `self`**: The original consumer will be unavailable after calling this method.
233    ///
234    /// Converts a reusable readonly bi-consumer to a one-time consumer that consumes itself on use.
235    /// This enables passing `BiConsumer` to functions that require `BiConsumerOnce`.
236    ///
237    /// # Returns
238    ///
239    /// Returns a `BoxBiConsumerOnce<T, U>`
240    fn into_once(self) -> BoxBiConsumerOnce<T, U>
241    where
242        Self: Sized + 'static,
243    {
244        BoxBiConsumerOnce::new(move |t, u| self.accept(t, u))
245    }
246
247    /// Converts to BoxBiConsumer (without consuming self)
248    ///
249    /// Creates a new `BoxBiConsumer` by cloning the current consumer.
250    /// The original consumer remains usable after this call.
251    ///
252    /// # Returns
253    ///
254    /// Returns a new `BoxBiConsumer<T, U>`
255    ///
256    /// # Examples
257    ///
258    /// ```rust
259    /// use qubit_function::{BiConsumer, RcBiConsumer};
260    ///
261    /// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
262    ///     println!("Sum: {}", x + y);
263    /// });
264    /// let box_consumer = consumer.to_box();
265    /// box_consumer.accept(&5, &3);
266    /// // Original consumer still usable
267    /// consumer.accept(&10, &20);
268    /// ```
269    fn to_box(&self) -> BoxBiConsumer<T, U>
270    where
271        Self: Clone + 'static,
272    {
273        self.clone().into_box()
274    }
275
276    /// Converts to RcBiConsumer (without consuming self)
277    ///
278    /// Creates a new `RcBiConsumer` by cloning the current consumer.
279    /// The original consumer remains usable after this call.
280    ///
281    /// # Returns
282    ///
283    /// Returns a new `RcBiConsumer<T, U>`
284    ///
285    /// # Examples
286    ///
287    /// ```rust
288    /// use qubit_function::{BiConsumer, ArcBiConsumer};
289    ///
290    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
291    ///     println!("Sum: {}", x + y);
292    /// });
293    /// let rc_consumer = consumer.to_rc();
294    /// rc_consumer.accept(&5, &3);
295    /// // Original consumer still usable
296    /// consumer.accept(&10, &20);
297    /// ```
298    fn to_rc(&self) -> RcBiConsumer<T, U>
299    where
300        Self: Clone + 'static,
301    {
302        self.clone().into_rc()
303    }
304
305    /// Converts to ArcBiConsumer (without consuming self)
306    ///
307    /// Creates a new `ArcBiConsumer` by cloning the current consumer.
308    /// The original consumer remains usable after this call.
309    ///
310    /// # Returns
311    ///
312    /// Returns a new `ArcBiConsumer<T, U>`
313    ///
314    /// # Examples
315    ///
316    /// ```rust
317    /// use qubit_function::{BiConsumer, RcBiConsumer};
318    ///
319    /// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
320    ///     println!("Sum: {}", x + y);
321    /// });
322    /// // Note: This will only compile if the closure is Send + Sync
323    /// // For demonstration, we use a simple closure
324    /// let arc_consumer = consumer.to_arc();
325    /// arc_consumer.accept(&5, &3);
326    /// ```
327    fn to_arc(&self) -> ArcBiConsumer<T, U>
328    where
329        Self: Clone + Send + Sync + 'static,
330    {
331        self.clone().into_arc()
332    }
333
334    /// Converts to a closure (without consuming self)
335    ///
336    /// Creates a new closure by cloning the current consumer.
337    /// The original consumer remains usable after this call.
338    ///
339    /// # Returns
340    ///
341    /// Returns a closure implementing `Fn(&T, &U)`
342    ///
343    /// # Examples
344    ///
345    /// ```rust
346    /// use qubit_function::{BiConsumer, RcBiConsumer};
347    ///
348    /// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
349    ///     println!("Sum: {}", x + y);
350    /// });
351    /// let func = consumer.to_fn();
352    /// func(&5, &3);
353    /// // Original consumer still usable
354    /// consumer.accept(&10, &20);
355    /// ```
356    fn to_fn(&self) -> impl Fn(&T, &U)
357    where
358        Self: Clone + 'static,
359    {
360        self.clone().into_fn()
361    }
362
363    /// Convert to BiConsumerOnce without consuming self
364    ///
365    /// **⚠️ Requires Clone**: This method requires `Self` to implement `Clone`.
366    /// Clones the current consumer and converts the clone to a one-time consumer.
367    ///
368    /// # Returns
369    ///
370    /// Returns a `BoxBiConsumerOnce<T, U>`
371    fn to_once(&self) -> BoxBiConsumerOnce<T, U>
372    where
373        Self: Clone + 'static,
374    {
375        self.clone().into_once()
376    }
377}
378
379// =======================================================================
380// 2. BoxBiConsumer - Single Ownership Implementation
381// =======================================================================
382
383/// BoxBiConsumer struct
384///
385/// A readonly bi-consumer implementation based on `Box<dyn Fn(&T, &U)>`
386/// for single ownership scenarios.
387///
388/// # Features
389///
390/// - **Single Ownership**: Not cloneable, ownership moves on use
391/// - **Zero Overhead**: No reference counting or locking
392/// - **Fully Immutable**: Neither modifies itself nor input values
393/// - **No Interior Mutability**: No need for Mutex or RefCell
394///
395/// # Use Cases
396///
397/// Choose `BoxBiConsumer` when:
398/// - The readonly bi-consumer is used only once or in a linear flow
399/// - No need to share the consumer across contexts
400/// - Pure observation operations like logging
401///
402/// # Examples
403///
404/// ```rust
405/// use qubit_function::{BiConsumer, BoxBiConsumer};
406///
407/// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
408///     println!("Sum: {}", x + y);
409/// });
410/// consumer.accept(&5, &3);
411/// ```
412///
413/// # Author
414///
415/// Haixing Hu
416pub struct BoxBiConsumer<T, U> {
417    function: Box<BiConsumerFn<T, U>>,
418    name: Option<String>,
419}
420
421impl<T, U> BoxBiConsumer<T, U> {
422    // Generates: new(), new_with_name(), name(), set_name(), noop()
423    impl_consumer_common_methods!(
424        BoxBiConsumer<T, U>,
425        (Fn(&T, &U) + 'static),
426        |f| Box::new(f)
427    );
428
429    // Generates: when() and and_then() methods that consume self
430    impl_box_consumer_methods!(
431        BoxBiConsumer<T, U>,
432        BoxConditionalBiConsumer,
433        BiConsumer
434    );
435}
436
437impl<T, U> BiConsumer<T, U> for BoxBiConsumer<T, U> {
438    fn accept(&self, first: &T, second: &U) {
439        (self.function)(first, second)
440    }
441
442    // Generates: into_box(), into_rc(), into_fn(), into_once()
443    impl_box_conversions!(
444        BoxBiConsumer<T, U>,
445        RcBiConsumer,
446        Fn(&T, &U),
447        BoxBiConsumerOnce
448    );
449}
450
451// Use macro to generate Debug and Display implementations
452impl_consumer_debug_display!(BoxBiConsumer<T, U>);
453
454// =======================================================================
455// 3. RcBiConsumer - Single-Threaded Shared Ownership
456// =======================================================================
457
458/// RcBiConsumer struct
459///
460/// A readonly bi-consumer implementation based on `Rc<dyn Fn(&T, &U)>`
461/// for single-threaded shared ownership scenarios. No need for RefCell
462/// because operations are readonly.
463///
464/// # Features
465///
466/// - **Shared Ownership**: Cloneable via `Rc`, multiple owners allowed
467/// - **Single-Threaded**: Not thread-safe, cannot send across threads
468/// - **No Interior Mutability Overhead**: No need for RefCell because
469///   readonly
470/// - **Non-Consuming API**: `and_then` borrows `&self`, original remains
471///   usable
472///
473/// # Use Cases
474///
475/// Choose `RcBiConsumer` when:
476/// - Need to share readonly bi-consumer within a single thread
477/// - Pure observation operations, performance critical
478/// - Single-threaded UI framework event handling
479///
480/// # Performance Advantages
481///
482/// `RcBiConsumer` has neither Arc's atomic operation overhead nor
483/// RefCell's runtime borrow checking overhead, making it the best
484/// performing among the three readonly bi-consumer types.
485///
486/// # Examples
487///
488/// ```rust
489/// use qubit_function::{BiConsumer, RcBiConsumer};
490///
491/// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
492///     println!("Sum: {}", x + y);
493/// });
494/// let clone = consumer.clone();
495///
496/// consumer.accept(&5, &3);
497/// clone.accept(&10, &20);
498/// ```
499///
500/// # Author
501///
502/// Haixing Hu
503pub struct RcBiConsumer<T, U> {
504    function: Rc<BiConsumerFn<T, U>>,
505    name: Option<String>,
506}
507
508impl<T, U> RcBiConsumer<T, U> {
509    // Generates: new(), new_with_name(), name(), set_name(), noop()
510    impl_consumer_common_methods!(
511        RcBiConsumer<T, U>,
512        (Fn(&T, &U) + 'static),
513        |f| Rc::new(f)
514    );
515
516    // Generates: when() and and_then() methods that borrow &self (Rc can clone)
517    impl_shared_consumer_methods!(
518        RcBiConsumer<T, U>,
519        RcConditionalBiConsumer,
520        into_rc,
521        BiConsumer,
522        'static
523    );
524}
525
526impl<T, U> BiConsumer<T, U> for RcBiConsumer<T, U> {
527    fn accept(&self, first: &T, second: &U) {
528        (self.function)(first, second)
529    }
530
531    // Use macro to implement conversion methods
532    impl_rc_conversions!(
533        RcBiConsumer<T, U>,
534        BoxBiConsumer,
535        BoxBiConsumerOnce,
536        Fn(t: &T, u: &U)
537    );
538}
539
540// Use macro to generate Clone implementation
541impl_consumer_clone!(RcBiConsumer<T, U>);
542
543// Use macro to generate Debug and Display implementations
544impl_consumer_debug_display!(RcBiConsumer<T, U>);
545
546// =======================================================================
547// 4. ArcBiConsumer - Thread-Safe Shared Ownership
548// =======================================================================
549
550/// ArcBiConsumer struct
551///
552/// A readonly bi-consumer implementation based on
553/// `Arc<dyn Fn(&T, &U) + Send + Sync>` for thread-safe shared ownership
554/// scenarios. No need for Mutex because operations are readonly.
555///
556/// # Features
557///
558/// - **Shared Ownership**: Cloneable via `Arc`, multiple owners allowed
559/// - **Thread-Safe**: Implements `Send + Sync`, safe for concurrent use
560/// - **No Locks**: Because readonly, no need for Mutex protection
561/// - **Non-Consuming API**: `and_then` borrows `&self`, original remains
562///   usable
563///
564/// # Use Cases
565///
566/// Choose `ArcBiConsumer` when:
567/// - Need to share readonly bi-consumer across multiple threads
568/// - Pure observation operations like logging, monitoring, notifications
569/// - Need high-concurrency reads without lock overhead
570///
571/// # Performance Advantages
572///
573/// Compared to `ArcBiConsumer`, `ArcBiConsumer` has no Mutex
574/// locking overhead, resulting in better performance in high-concurrency
575/// scenarios.
576///
577/// # Examples
578///
579/// ```rust
580/// use qubit_function::{BiConsumer, ArcBiConsumer};
581///
582/// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
583///     println!("Sum: {}", x + y);
584/// });
585/// let clone = consumer.clone();
586///
587/// consumer.accept(&5, &3);
588/// clone.accept(&10, &20);
589/// ```
590///
591/// # Author
592///
593/// Haixing Hu
594pub struct ArcBiConsumer<T, U> {
595    function: Arc<ThreadSafeBiConsumerFn<T, U>>,
596    name: Option<String>,
597}
598
599impl<T, U> ArcBiConsumer<T, U> {
600    // Generates: new(), new_with_name(), name(), set_name(), noop()
601    impl_consumer_common_methods!(
602        ArcBiConsumer<T, U>,
603        (Fn(&T, &U) + Send + Sync + 'static),
604        |f| Arc::new(f)
605    );
606
607    // Generates: when() and and_then() methods that borrow &self (Arc can clone)
608    impl_shared_consumer_methods!(
609        ArcBiConsumer<T, U>,
610        ArcConditionalBiConsumer,
611        into_arc,
612        BiConsumer,
613        Send + Sync + 'static
614    );
615}
616
617impl<T, U> BiConsumer<T, U> for ArcBiConsumer<T, U> {
618    fn accept(&self, first: &T, second: &U) {
619        (self.function)(first, second)
620    }
621
622    // Use macro to implement conversion methods
623    impl_arc_conversions!(
624        ArcBiConsumer<T, U>,
625        BoxBiConsumer,
626        RcBiConsumer,
627        BoxBiConsumerOnce,
628        Fn(t: &T, u: &U)
629    );
630}
631
632// Use macro to generate Clone implementation
633impl_consumer_clone!(ArcBiConsumer<T, U>);
634
635// Use macro to generate Debug and Display implementations
636impl_consumer_debug_display!(ArcBiConsumer<T, U>);
637
638// =======================================================================
639// 5. Implement BiConsumer trait for closures
640// =======================================================================
641
642// Implements BiConsumer for all Fn(&T, &U)
643impl_closure_trait!(
644    BiConsumer<T, U>,
645    accept,
646    BoxBiConsumerOnce,
647    Fn(first: &T, second: &U)
648);
649
650// =======================================================================
651// 6. Provide extension methods for closures
652// =======================================================================
653
654/// Extension trait providing readonly bi-consumer composition methods for
655/// closures
656///
657/// Provides `and_then` and other composition methods for all closures
658/// implementing `Fn(&T, &U)`, enabling direct method chaining on closures
659/// without explicit wrapper types.
660///
661/// # Features
662///
663/// - **Natural Syntax**: Chain operations directly on closures
664/// - **Returns BoxBiConsumer**: Composition results can be
665///   further chained
666/// - **Zero Cost**: No overhead when composing closures
667/// - **Automatic Implementation**: All `Fn(&T, &U)` closures get these
668///   methods automatically
669///
670/// # Examples
671///
672/// ```rust
673/// use qubit_function::{BiConsumer, FnBiConsumerOps};
674///
675/// let chained = (|x: &i32, y: &i32| {
676///     println!("First: {}, {}", x, y);
677/// }).and_then(|x: &i32, y: &i32| {
678///     println!("Second: sum = {}", x + y);
679/// });
680/// chained.accept(&5, &3);
681/// ```
682///
683/// # Author
684///
685/// Haixing Hu
686pub trait FnBiConsumerOps<T, U>: Fn(&T, &U) + Sized {
687    /// Chains another readonly bi-consumer in sequence
688    ///
689    /// Returns a new consumer executing the current operation first, then
690    /// the next operation. Consumes the current closure and returns
691    /// `BoxBiConsumer<T, U>`.
692    ///
693    /// # Type Parameters
694    ///
695    /// * `C` - The type of the next consumer
696    ///
697    /// # Parameters
698    ///
699    /// * `next` - The consumer to execute after the current operation
700    ///
701    /// # Returns
702    ///
703    /// Returns the composed `BoxBiConsumer<T, U>`
704    ///
705    /// # Examples
706    ///
707    /// ```rust
708    /// use qubit_function::{BiConsumer, FnBiConsumerOps};
709    ///
710    /// let chained = (|x: &i32, y: &i32| {
711    ///     println!("First: {}, {}", x, y);
712    /// }).and_then(|x: &i32, y: &i32| {
713    ///     println!("Second: sum = {}", x + y);
714    /// }).and_then(|x: &i32, y: &i32| {
715    ///     println!("Third: product = {}", x * y);
716    /// });
717    ///
718    /// chained.accept(&5, &3);
719    /// ```
720    fn and_then<C>(self, next: C) -> BoxBiConsumer<T, U>
721    where
722        Self: 'static,
723        C: BiConsumer<T, U> + 'static,
724        T: 'static,
725        U: 'static,
726    {
727        let first = self;
728        let second = next;
729        BoxBiConsumer::new(move |t, u| {
730            first(t, u);
731            second.accept(t, u);
732        })
733    }
734}
735
736/// Implements FnBiConsumerOps for all closure types
737impl<T, U, F> FnBiConsumerOps<T, U> for F where F: Fn(&T, &U) {}
738
739// =======================================================================
740// 7. BoxConditionalBiConsumer - Box-based Conditional BiConsumer
741// =======================================================================
742
743/// BoxConditionalBiConsumer struct
744///
745/// A conditional readonly bi-consumer that only executes when a predicate is satisfied.
746/// Uses `BoxBiConsumer` and `BoxBiPredicate` for single ownership semantics.
747///
748/// This type is typically created by calling `BoxBiConsumer::when()` and is
749/// designed to work with the `or_else()` method to create if-then-else logic.
750///
751/// # Features
752///
753/// - **Single Ownership**: Not cloneable, consumes `self` on use
754/// - **Conditional Execution**: Only consumes when predicate returns `true`
755/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
756/// - **Implements BiConsumer**: Can be used anywhere a `BiConsumer` is expected
757/// - **Readonly**: Neither modifies itself nor input values
758///
759/// # Examples
760///
761/// ## Basic Conditional Execution
762///
763/// ```rust
764/// use qubit_function::{BiConsumer, BoxBiConsumer};
765///
766/// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
767///     println!("Both positive: {} + {} = {}", x, y, x + y);
768/// });
769/// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
770///
771/// conditional.accept(&5, &3);  // Prints: Both positive: 5 + 3 = 8
772/// conditional.accept(&-5, &3); // Does nothing
773/// ```
774///
775/// ## With or_else Branch
776///
777/// ```rust
778/// use qubit_function::{BiConsumer, BoxBiConsumer};
779///
780/// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
781///     println!("Both positive: {} + {} = {}", x, y, x + y);
782/// })
783/// .when(|x: &i32, y: &i32| *x > 0 && *y > 0)
784/// .or_else(|x: &i32, y: &i32| {
785///     println!("Not both positive: {} and {}", x, y);
786/// });
787///
788/// consumer.accept(&5, &3);  // Prints: Both positive: 5 + 3 = 8
789/// consumer.accept(&-5, &3); // Prints: Not both positive: -5 and 3
790/// ```
791///
792/// # Author
793///
794/// Haixing Hu
795pub struct BoxConditionalBiConsumer<T, U> {
796    consumer: BoxBiConsumer<T, U>,
797    predicate: BoxBiPredicate<T, U>,
798}
799
800// Use macro to generate conditional bi-consumer implementations
801impl_box_conditional_consumer!(
802    BoxConditionalBiConsumer<T, U>,
803    BoxBiConsumer,
804    BiConsumer
805);
806
807// Hand-written BiConsumer trait implementation
808impl<T, U> BiConsumer<T, U> for BoxConditionalBiConsumer<T, U> {
809    fn accept(&self, first: &T, second: &U) {
810        if self.predicate.test(first, second) {
811            self.consumer.accept(first, second);
812        }
813    }
814
815    // Generates: into_box(), into_rc(), into_fn()
816    impl_conditional_consumer_conversions!(
817        BoxBiConsumer<T, U>,
818        RcBiConsumer,
819        Fn
820    );
821}
822
823// Use macro to generate Debug and Display implementations
824impl_conditional_consumer_debug_display!(BoxConditionalBiConsumer<T, U>);
825
826// =======================================================================
827// 8. ArcConditionalBiConsumer - Arc-based Conditional BiConsumer
828// =======================================================================
829
830/// ArcConditionalBiConsumer struct
831///
832/// A conditional bi-consumer that wraps an `ArcBiConsumer` and only executes
833/// when a predicate is satisfied. Based on `Arc` for thread-safe shared ownership.
834///
835/// # Features
836///
837/// - **Shared Ownership**: Cloneable through `Arc`, allows multiple owners
838/// - **Thread Safe**: Implements `Send + Sync`, can be safely used concurrently
839/// - **Conditional Execution**: Only consumes when predicate returns `true`
840/// - **Implements BiConsumer**: Can be used anywhere a `BiConsumer` is expected
841/// - **Readonly**: Neither modifies itself nor input values
842///
843/// # Author
844///
845/// Haixing Hu
846pub struct ArcConditionalBiConsumer<T, U> {
847    consumer: ArcBiConsumer<T, U>,
848    predicate: ArcBiPredicate<T, U>,
849}
850
851// Use macro to generate conditional bi-consumer implementations
852impl_shared_conditional_consumer!(
853    ArcConditionalBiConsumer<T, U>,
854    ArcBiConsumer,
855    BiConsumer,
856    into_arc,
857    Send + Sync + 'static
858);
859
860// Hand-written BiConsumer trait implementation
861impl<T, U> BiConsumer<T, U> for ArcConditionalBiConsumer<T, U> {
862    fn accept(&self, first: &T, second: &U) {
863        if self.predicate.test(first, second) {
864            self.consumer.accept(first, second);
865        }
866    }
867
868    // Generates: into_box(), into_rc(), into_fn()
869    impl_conditional_consumer_conversions!(
870        BoxBiConsumer<T, U>,
871        RcBiConsumer,
872        Fn
873    );
874}
875
876// Use macro to generate Clone implementation
877impl_conditional_consumer_clone!(ArcConditionalBiConsumer<T, U>);
878
879// Use macro to generate Debug and Display implementations
880impl_conditional_consumer_debug_display!(ArcConditionalBiConsumer<T, U>);
881
882// =======================================================================
883// 9. RcConditionalBiConsumer - Rc-based Conditional BiConsumer
884// =======================================================================
885
886/// RcConditionalBiConsumer struct
887///
888/// A conditional bi-consumer that wraps an `RcBiConsumer` and only executes
889/// when a predicate is satisfied. Based on `Rc` for single-threaded shared ownership.
890///
891/// # Features
892///
893/// - **Shared Ownership**: Cloneable through `Rc`, allows multiple owners
894/// - **Single-Threaded**: Not thread-safe, more efficient than Arc in single-threaded contexts
895/// - **Conditional Execution**: Only consumes when predicate returns `true`
896/// - **Implements BiConsumer**: Can be used anywhere a `BiConsumer` is expected
897/// - **Readonly**: Neither modifies itself nor input values
898///
899/// # Author
900///
901/// Haixing Hu
902pub struct RcConditionalBiConsumer<T, U> {
903    consumer: RcBiConsumer<T, U>,
904    predicate: RcBiPredicate<T, U>,
905}
906
907// Use macro to generate conditional bi-consumer implementations
908impl_shared_conditional_consumer!(
909    RcConditionalBiConsumer<T, U>,
910    RcBiConsumer,
911    BiConsumer,
912    into_rc,
913    'static
914);
915
916// Hand-written BiConsumer trait implementation
917impl<T, U> BiConsumer<T, U> for RcConditionalBiConsumer<T, U> {
918    fn accept(&self, first: &T, second: &U) {
919        if self.predicate.test(first, second) {
920            self.consumer.accept(first, second);
921        }
922    }
923
924    // Generates: into_box(), into_rc(), into_fn()
925    impl_conditional_consumer_conversions!(
926        BoxBiConsumer<T, U>,
927        RcBiConsumer,
928        Fn
929    );
930}
931
932// Use macro to generate Clone implementation
933impl_conditional_consumer_clone!(RcConditionalBiConsumer<T, U>);
934
935// Use macro to generate Debug and Display implementations
936impl_conditional_consumer_debug_display!(RcConditionalBiConsumer<T, U>);