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, ArcBiConsumer};
260 ///
261 /// let consumer = ArcBiConsumer::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, ArcBiConsumer};
318 ///
319 /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
320 /// println!("Sum: {}", x + y);
321 /// });
322 /// let arc_consumer = consumer.to_arc();
323 /// arc_consumer.accept(&5, &3);
324 /// ```
325 fn to_arc(&self) -> ArcBiConsumer<T, U>
326 where
327 Self: Clone + Send + Sync + 'static,
328 {
329 self.clone().into_arc()
330 }
331
332 /// Converts to a closure (without consuming self)
333 ///
334 /// Creates a new closure by cloning the current consumer.
335 /// The original consumer remains usable after this call.
336 ///
337 /// # Returns
338 ///
339 /// Returns a closure implementing `Fn(&T, &U)`
340 ///
341 /// # Examples
342 ///
343 /// ```rust
344 /// use qubit_function::{BiConsumer, RcBiConsumer};
345 ///
346 /// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
347 /// println!("Sum: {}", x + y);
348 /// });
349 /// let func = consumer.to_fn();
350 /// func(&5, &3);
351 /// // Original consumer still usable
352 /// consumer.accept(&10, &20);
353 /// ```
354 fn to_fn(&self) -> impl Fn(&T, &U)
355 where
356 Self: Clone + 'static,
357 {
358 self.clone().into_fn()
359 }
360
361 /// Convert to BiConsumerOnce without consuming self
362 ///
363 /// **⚠️ Requires Clone**: This method requires `Self` to implement `Clone`.
364 /// Clones the current consumer and converts the clone to a one-time consumer.
365 ///
366 /// # Returns
367 ///
368 /// Returns a `BoxBiConsumerOnce<T, U>`
369 fn to_once(&self) -> BoxBiConsumerOnce<T, U>
370 where
371 Self: Clone + 'static,
372 {
373 self.clone().into_once()
374 }
375}
376
377// =======================================================================
378// 2. BoxBiConsumer - Single Ownership Implementation
379// =======================================================================
380
381/// BoxBiConsumer struct
382///
383/// A readonly bi-consumer implementation based on `Box<dyn Fn(&T, &U)>`
384/// for single ownership scenarios.
385///
386/// # Features
387///
388/// - **Single Ownership**: Not cloneable, ownership moves on use
389/// - **Zero Overhead**: No reference counting or locking
390/// - **Fully Immutable**: Neither modifies itself nor input values
391/// - **No Interior Mutability**: No need for Mutex or RefCell
392///
393/// # Use Cases
394///
395/// Choose `BoxBiConsumer` when:
396/// - The readonly bi-consumer is used only once or in a linear flow
397/// - No need to share the consumer across contexts
398/// - Pure observation operations like logging
399///
400/// # Examples
401///
402/// ```rust
403/// use qubit_function::{BiConsumer, BoxBiConsumer};
404///
405/// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
406/// println!("Sum: {}", x + y);
407/// });
408/// consumer.accept(&5, &3);
409/// ```
410///
411/// # Author
412///
413/// Haixing Hu
414pub struct BoxBiConsumer<T, U> {
415 function: Box<BiConsumerFn<T, U>>,
416 name: Option<String>,
417}
418
419impl<T, U> BoxBiConsumer<T, U> {
420 // Generates: new(), new_with_name(), name(), set_name(), noop()
421 impl_consumer_common_methods!(
422 BoxBiConsumer<T, U>,
423 (Fn(&T, &U) + 'static),
424 |f| Box::new(f)
425 );
426
427 // Generates: when() and and_then() methods that consume self
428 impl_box_consumer_methods!(
429 BoxBiConsumer<T, U>,
430 BoxConditionalBiConsumer,
431 BiConsumer
432 );
433}
434
435impl<T, U> BiConsumer<T, U> for BoxBiConsumer<T, U> {
436 fn accept(&self, first: &T, second: &U) {
437 (self.function)(first, second)
438 }
439
440 // Generates: into_box(), into_rc(), into_fn(), into_once()
441 impl_box_conversions!(
442 BoxBiConsumer<T, U>,
443 RcBiConsumer,
444 Fn(&T, &U),
445 BoxBiConsumerOnce
446 );
447}
448
449// Use macro to generate Debug and Display implementations
450impl_consumer_debug_display!(BoxBiConsumer<T, U>);
451
452// =======================================================================
453// 3. RcBiConsumer - Single-Threaded Shared Ownership
454// =======================================================================
455
456/// RcBiConsumer struct
457///
458/// A readonly bi-consumer implementation based on `Rc<dyn Fn(&T, &U)>`
459/// for single-threaded shared ownership scenarios. No need for RefCell
460/// because operations are readonly.
461///
462/// # Features
463///
464/// - **Shared Ownership**: Cloneable via `Rc`, multiple owners allowed
465/// - **Single-Threaded**: Not thread-safe, cannot send across threads
466/// - **No Interior Mutability Overhead**: No need for RefCell because
467/// readonly
468/// - **Non-Consuming API**: `and_then` borrows `&self`, original remains
469/// usable
470///
471/// # Use Cases
472///
473/// Choose `RcBiConsumer` when:
474/// - Need to share readonly bi-consumer within a single thread
475/// - Pure observation operations, performance critical
476/// - Single-threaded UI framework event handling
477///
478/// # Performance Advantages
479///
480/// `RcBiConsumer` has neither Arc's atomic operation overhead nor
481/// RefCell's runtime borrow checking overhead, making it the best
482/// performing among the three readonly bi-consumer types.
483///
484/// # Examples
485///
486/// ```rust
487/// use qubit_function::{BiConsumer, ArcBiConsumer};
488///
489/// let consumer: ArcBiConsumer<i32, i32> = ArcBiConsumer::new(|x: &i32, y: &i32| {
490/// println!("Sum: {}", x + y);
491/// });
492/// let clone = consumer.clone();
493///
494/// consumer.accept(&5, &3);
495/// clone.accept(&10, &20);
496/// ```
497///
498/// # Author
499///
500/// Haixing Hu
501pub struct RcBiConsumer<T, U> {
502 function: Rc<BiConsumerFn<T, U>>,
503 name: Option<String>,
504}
505
506impl<T, U> RcBiConsumer<T, U> {
507 // Generates: new(), new_with_name(), name(), set_name(), noop()
508 impl_consumer_common_methods!(
509 RcBiConsumer<T, U>,
510 (Fn(&T, &U) + 'static),
511 |f| Rc::new(f)
512 );
513
514 // Generates: when() and and_then() methods that borrow &self (Rc can clone)
515 impl_shared_consumer_methods!(
516 RcBiConsumer<T, U>,
517 RcConditionalBiConsumer,
518 into_rc,
519 BiConsumer,
520 'static
521 );
522}
523
524impl<T, U> BiConsumer<T, U> for RcBiConsumer<T, U> {
525 fn accept(&self, first: &T, second: &U) {
526 (self.function)(first, second)
527 }
528
529 // Use macro to implement conversion methods
530 impl_rc_conversions!(
531 RcBiConsumer<T, U>,
532 BoxBiConsumer,
533 BoxBiConsumerOnce,
534 Fn(t: &T, u: &U)
535 );
536}
537
538// Use macro to generate Clone implementation
539impl_consumer_clone!(RcBiConsumer<T, U>);
540
541// Use macro to generate Debug and Display implementations
542impl_consumer_debug_display!(RcBiConsumer<T, U>);
543
544// =======================================================================
545// 4. ArcBiConsumer - Thread-Safe Shared Ownership
546// =======================================================================
547
548/// ArcBiConsumer struct
549///
550/// A readonly bi-consumer implementation based on
551/// `Arc<dyn Fn(&T, &U) + Send + Sync>` for thread-safe shared ownership
552/// scenarios. No need for Mutex because operations are readonly.
553///
554/// # Features
555///
556/// - **Shared Ownership**: Cloneable via `Arc`, multiple owners allowed
557/// - **Thread-Safe**: Implements `Send + Sync`, safe for concurrent use
558/// - **No Locks**: Because readonly, no need for Mutex protection
559/// - **Non-Consuming API**: `and_then` borrows `&self`, original remains
560/// usable
561///
562/// # Use Cases
563///
564/// Choose `ArcBiConsumer` when:
565/// - Need to share readonly bi-consumer across multiple threads
566/// - Pure observation operations like logging, monitoring, notifications
567/// - Need high-concurrency reads without lock overhead
568///
569/// # Performance Advantages
570///
571/// Compared to `ArcBiConsumer`, `ArcBiConsumer` has no Mutex
572/// locking overhead, resulting in better performance in high-concurrency
573/// scenarios.
574///
575/// # Examples
576///
577/// ```rust
578/// use qubit_function::{BiConsumer, ArcBiConsumer};
579///
580/// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
581/// println!("Sum: {}", x + y);
582/// });
583/// let clone = consumer.clone();
584///
585/// consumer.accept(&5, &3);
586/// clone.accept(&10, &20);
587/// ```
588///
589/// # Author
590///
591/// Haixing Hu
592pub struct ArcBiConsumer<T, U> {
593 function: Arc<ThreadSafeBiConsumerFn<T, U>>,
594 name: Option<String>,
595}
596
597impl<T, U> ArcBiConsumer<T, U> {
598 // Generates: new(), new_with_name(), name(), set_name(), noop()
599 impl_consumer_common_methods!(
600 ArcBiConsumer<T, U>,
601 (Fn(&T, &U) + Send + Sync + 'static),
602 |f| Arc::new(f)
603 );
604
605 // Generates: when() and and_then() methods that borrow &self (Arc can clone)
606 impl_shared_consumer_methods!(
607 ArcBiConsumer<T, U>,
608 ArcConditionalBiConsumer,
609 into_arc,
610 BiConsumer,
611 Send + Sync + 'static
612 );
613}
614
615impl<T, U> BiConsumer<T, U> for ArcBiConsumer<T, U> {
616 fn accept(&self, first: &T, second: &U) {
617 (self.function)(first, second)
618 }
619
620 // Use macro to implement conversion methods
621 impl_arc_conversions!(
622 ArcBiConsumer<T, U>,
623 BoxBiConsumer,
624 RcBiConsumer,
625 BoxBiConsumerOnce,
626 Fn(t: &T, u: &U)
627 );
628}
629
630// Use macro to generate Clone implementation
631impl_consumer_clone!(ArcBiConsumer<T, U>);
632
633// Use macro to generate Debug and Display implementations
634impl_consumer_debug_display!(ArcBiConsumer<T, U>);
635
636// =======================================================================
637// 5. Implement BiConsumer trait for closures
638// =======================================================================
639
640// Implements BiConsumer for all Fn(&T, &U)
641impl_closure_trait!(
642 BiConsumer<T, U>,
643 accept,
644 BoxBiConsumerOnce,
645 Fn(first: &T, second: &U)
646);
647
648// =======================================================================
649// 6. Provide extension methods for closures
650// =======================================================================
651
652/// Extension trait providing readonly bi-consumer composition methods for
653/// closures
654///
655/// Provides `and_then` and other composition methods for all closures
656/// implementing `Fn(&T, &U)`, enabling direct method chaining on closures
657/// without explicit wrapper types.
658///
659/// # Features
660///
661/// - **Natural Syntax**: Chain operations directly on closures
662/// - **Returns BoxBiConsumer**: Composition results can be
663/// further chained
664/// - **Zero Cost**: No overhead when composing closures
665/// - **Automatic Implementation**: All `Fn(&T, &U)` closures get these
666/// methods automatically
667///
668/// # Examples
669///
670/// ```rust
671/// use qubit_function::{BiConsumer, FnBiConsumerOps};
672///
673/// let chained = (|x: &i32, y: &i32| {
674/// println!("First: {}, {}", x, y);
675/// }).and_then(|x: &i32, y: &i32| {
676/// println!("Second: sum = {}", x + y);
677/// });
678/// chained.accept(&5, &3);
679/// ```
680///
681/// # Author
682///
683/// Haixing Hu
684pub trait FnBiConsumerOps<T, U>: Fn(&T, &U) + Sized {
685 /// Chains another readonly bi-consumer in sequence
686 ///
687 /// Returns a new consumer executing the current operation first, then
688 /// the next operation. Consumes the current closure and returns
689 /// `BoxBiConsumer<T, U>`.
690 ///
691 /// # Type Parameters
692 ///
693 /// * `C` - The type of the next consumer
694 ///
695 /// # Parameters
696 ///
697 /// * `next` - The consumer to execute after the current operation
698 ///
699 /// # Returns
700 ///
701 /// Returns the composed `BoxBiConsumer<T, U>`
702 ///
703 /// # Examples
704 ///
705 /// ```rust
706 /// use qubit_function::{BiConsumer, FnBiConsumerOps};
707 ///
708 /// let chained = (|x: &i32, y: &i32| {
709 /// println!("First: {}, {}", x, y);
710 /// }).and_then(|x: &i32, y: &i32| {
711 /// println!("Second: sum = {}", x + y);
712 /// }).and_then(|x: &i32, y: &i32| {
713 /// println!("Third: product = {}", x * y);
714 /// });
715 ///
716 /// chained.accept(&5, &3);
717 /// ```
718 fn and_then<C>(self, next: C) -> BoxBiConsumer<T, U>
719 where
720 Self: 'static,
721 C: BiConsumer<T, U> + 'static,
722 T: 'static,
723 U: 'static,
724 {
725 let first = self;
726 let second = next;
727 BoxBiConsumer::new(move |t, u| {
728 first(t, u);
729 second.accept(t, u);
730 })
731 }
732}
733
734/// Implements FnBiConsumerOps for all closure types
735impl<T, U, F> FnBiConsumerOps<T, U> for F where F: Fn(&T, &U) {}
736
737// =======================================================================
738// 7. BoxConditionalBiConsumer - Box-based Conditional BiConsumer
739// =======================================================================
740
741/// BoxConditionalBiConsumer struct
742///
743/// A conditional readonly bi-consumer that only executes when a predicate is satisfied.
744/// Uses `BoxBiConsumer` and `BoxBiPredicate` for single ownership semantics.
745///
746/// This type is typically created by calling `BoxBiConsumer::when()` and is
747/// designed to work with the `or_else()` method to create if-then-else logic.
748///
749/// # Features
750///
751/// - **Single Ownership**: Not cloneable, consumes `self` on use
752/// - **Conditional Execution**: Only consumes when predicate returns `true`
753/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
754/// - **Implements BiConsumer**: Can be used anywhere a `BiConsumer` is expected
755/// - **Readonly**: Neither modifies itself nor input values
756///
757/// # Examples
758///
759/// ## Basic Conditional Execution
760///
761/// ```rust
762/// use qubit_function::{BiConsumer, BoxBiConsumer};
763///
764/// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
765/// println!("Both positive: {} + {} = {}", x, y, x + y);
766/// });
767/// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
768///
769/// conditional.accept(&5, &3); // Prints: Both positive: 5 + 3 = 8
770/// conditional.accept(&-5, &3); // Does nothing
771/// ```
772///
773/// ## With or_else Branch
774///
775/// ```rust
776/// use qubit_function::{BiConsumer, BoxBiConsumer};
777///
778/// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
779/// println!("Both positive: {} + {} = {}", x, y, x + y);
780/// })
781/// .when(|x: &i32, y: &i32| *x > 0 && *y > 0)
782/// .or_else(|x: &i32, y: &i32| {
783/// println!("Not both positive: {} and {}", x, y);
784/// });
785///
786/// consumer.accept(&5, &3); // Prints: Both positive: 5 + 3 = 8
787/// consumer.accept(&-5, &3); // Prints: Not both positive: -5 and 3
788/// ```
789///
790/// # Author
791///
792/// Haixing Hu
793pub struct BoxConditionalBiConsumer<T, U> {
794 consumer: BoxBiConsumer<T, U>,
795 predicate: BoxBiPredicate<T, U>,
796}
797
798// Use macro to generate conditional bi-consumer implementations
799impl_box_conditional_consumer!(
800 BoxConditionalBiConsumer<T, U>,
801 BoxBiConsumer,
802 BiConsumer
803);
804
805// Hand-written BiConsumer trait implementation
806impl<T, U> BiConsumer<T, U> for BoxConditionalBiConsumer<T, U> {
807 fn accept(&self, first: &T, second: &U) {
808 if self.predicate.test(first, second) {
809 self.consumer.accept(first, second);
810 }
811 }
812
813 // Generates: into_box(), into_rc(), into_fn()
814 impl_conditional_consumer_conversions!(
815 BoxBiConsumer<T, U>,
816 RcBiConsumer,
817 Fn
818 );
819}
820
821// Use macro to generate Debug and Display implementations
822impl_conditional_consumer_debug_display!(BoxConditionalBiConsumer<T, U>);
823
824// =======================================================================
825// 8. ArcConditionalBiConsumer - Arc-based Conditional BiConsumer
826// =======================================================================
827
828/// ArcConditionalBiConsumer struct
829///
830/// A conditional bi-consumer that wraps an `ArcBiConsumer` and only executes
831/// when a predicate is satisfied. Based on `Arc` for thread-safe shared ownership.
832///
833/// # Features
834///
835/// - **Shared Ownership**: Cloneable through `Arc`, allows multiple owners
836/// - **Thread Safe**: Implements `Send + Sync`, can be safely used concurrently
837/// - **Conditional Execution**: Only consumes when predicate returns `true`
838/// - **Implements BiConsumer**: Can be used anywhere a `BiConsumer` is expected
839/// - **Readonly**: Neither modifies itself nor input values
840///
841/// # Author
842///
843/// Haixing Hu
844pub struct ArcConditionalBiConsumer<T, U> {
845 consumer: ArcBiConsumer<T, U>,
846 predicate: ArcBiPredicate<T, U>,
847}
848
849// Use macro to generate conditional bi-consumer implementations
850impl_shared_conditional_consumer!(
851 ArcConditionalBiConsumer<T, U>,
852 ArcBiConsumer,
853 BiConsumer,
854 into_arc,
855 Send + Sync + 'static
856);
857
858// Hand-written BiConsumer trait implementation
859impl<T, U> BiConsumer<T, U> for ArcConditionalBiConsumer<T, U> {
860 fn accept(&self, first: &T, second: &U) {
861 if self.predicate.test(first, second) {
862 self.consumer.accept(first, second);
863 }
864 }
865
866 // Generates: into_box(), into_rc(), into_fn()
867 impl_conditional_consumer_conversions!(
868 BoxBiConsumer<T, U>,
869 RcBiConsumer,
870 Fn
871 );
872}
873
874// Use macro to generate Clone implementation
875impl_conditional_consumer_clone!(ArcConditionalBiConsumer<T, U>);
876
877// Use macro to generate Debug and Display implementations
878impl_conditional_consumer_debug_display!(ArcConditionalBiConsumer<T, U>);
879
880// =======================================================================
881// 9. RcConditionalBiConsumer - Rc-based Conditional BiConsumer
882// =======================================================================
883
884/// RcConditionalBiConsumer struct
885///
886/// A conditional bi-consumer that wraps an `RcBiConsumer` and only executes
887/// when a predicate is satisfied. Based on `Rc` for single-threaded shared ownership.
888///
889/// # Features
890///
891/// - **Shared Ownership**: Cloneable through `Rc`, allows multiple owners
892/// - **Single-Threaded**: Not thread-safe, more efficient than Arc in single-threaded contexts
893/// - **Conditional Execution**: Only consumes when predicate returns `true`
894/// - **Implements BiConsumer**: Can be used anywhere a `BiConsumer` is expected
895/// - **Readonly**: Neither modifies itself nor input values
896///
897/// # Author
898///
899/// Haixing Hu
900pub struct RcConditionalBiConsumer<T, U> {
901 consumer: RcBiConsumer<T, U>,
902 predicate: RcBiPredicate<T, U>,
903}
904
905// Use macro to generate conditional bi-consumer implementations
906impl_shared_conditional_consumer!(
907 RcConditionalBiConsumer<T, U>,
908 RcBiConsumer,
909 BiConsumer,
910 into_rc,
911 'static
912);
913
914// Hand-written BiConsumer trait implementation
915impl<T, U> BiConsumer<T, U> for RcConditionalBiConsumer<T, U> {
916 fn accept(&self, first: &T, second: &U) {
917 if self.predicate.test(first, second) {
918 self.consumer.accept(first, second);
919 }
920 }
921
922 // Generates: into_box(), into_rc(), into_fn()
923 impl_conditional_consumer_conversions!(
924 BoxBiConsumer<T, U>,
925 RcBiConsumer,
926 Fn
927 );
928}
929
930// Use macro to generate Clone implementation
931impl_conditional_consumer_clone!(RcConditionalBiConsumer<T, U>);
932
933// Use macro to generate Debug and Display implementations
934impl_conditional_consumer_debug_display!(RcConditionalBiConsumer<T, U>);