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