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>);