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
80mod box_bi_consumer;
81pub use box_bi_consumer::BoxBiConsumer;
82mod rc_bi_consumer;
83pub use rc_bi_consumer::RcBiConsumer;
84mod arc_bi_consumer;
85pub use arc_bi_consumer::ArcBiConsumer;
86mod fn_bi_consumer_ops;
87pub use fn_bi_consumer_ops::FnBiConsumerOps;
88mod box_conditional_bi_consumer;
89pub use box_conditional_bi_consumer::BoxConditionalBiConsumer;
90mod arc_conditional_bi_consumer;
91pub use arc_conditional_bi_consumer::ArcConditionalBiConsumer;
92mod rc_conditional_bi_consumer;
93pub use rc_conditional_bi_consumer::RcConditionalBiConsumer;
94
95// =======================================================================
96// 1. BiConsumer Trait - Unified Interface
97// =======================================================================
98
99/// BiConsumer trait - Unified non-mutating bi-consumer interface
100///
101/// It is similar to the `Fn(&T, &U)` trait in the standard library.
102///
103/// Defines core behavior for all non-mutating bi-consumer types. The API uses
104/// `&self` and shared input references, so callers can use a bi-consumer
105/// without granting mutable access to the consumer wrapper or input values.
106///
107/// # Automatic Implementations
108///
109/// - All closures implementing `Fn(&T, &U)`
110/// - `BoxBiConsumer<T, U>`, `ArcBiConsumer<T, U>`,
111///   `RcBiConsumer<T, U>`
112///
113/// # Features
114///
115/// - **Unified Interface**: All non-mutating bi-consumer types share the same
116///   `accept` method signature
117/// - **Automatic Implementation**: Closures automatically implement this
118///   trait with zero overhead
119/// - **Type Conversions**: Easy conversion between ownership models
120/// - **Generic Programming**: Write functions accepting any non-mutating
121///   bi-consumer type
122/// - **No Wrapper Interior Mutability**: No need for Mutex or RefCell in the
123///   wrapper, making shared ownership more efficient
124///
125/// # Examples
126///
127/// ```rust
128/// use qubit_function::{BiConsumer, BoxBiConsumer};
129///
130/// fn apply_consumer<C: BiConsumer<i32, i32>>(
131///     consumer: &C,
132///     a: &i32,
133///     b: &i32
134/// ) {
135///     consumer.accept(a, b);
136/// }
137///
138/// let box_con = BoxBiConsumer::new(|x: &i32, y: &i32| {
139///     println!("Sum: {}", x + y);
140/// });
141/// apply_consumer(&box_con, &5, &3);
142/// ```
143///
144/// # Author
145///
146/// Haixing Hu
147pub trait BiConsumer<T, U> {
148    /// Performs the non-mutating consumption operation
149    ///
150    /// Executes an operation on the given two references. The operation
151    /// typically reads input values or produces side effects, but neither
152    /// modifies the input values nor the consumer's own state.
153    ///
154    /// # Parameters
155    ///
156    /// * `first` - Reference to the first value to consume
157    /// * `second` - Reference to the second value to consume
158    ///
159    /// # Examples
160    ///
161    /// ```rust
162    /// use qubit_function::{BiConsumer, BoxBiConsumer};
163    ///
164    /// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
165    ///     println!("Values: {}, {}", x, y);
166    /// });
167    /// consumer.accept(&5, &3);
168    /// ```
169    fn accept(&self, first: &T, second: &U);
170
171    /// Converts to BoxBiConsumer
172    ///
173    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
174    /// calling this method.
175    ///
176    /// # Returns
177    ///
178    /// Returns the wrapped `BoxBiConsumer<T, U>`
179    fn into_box(self) -> BoxBiConsumer<T, U>
180    where
181        Self: Sized + 'static,
182    {
183        BoxBiConsumer::new(move |t, u| self.accept(t, u))
184    }
185
186    /// Converts to RcBiConsumer
187    ///
188    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
189    /// calling this method.
190    ///
191    /// # Returns
192    ///
193    /// Returns the wrapped `RcBiConsumer<T, U>`
194    fn into_rc(self) -> RcBiConsumer<T, U>
195    where
196        Self: Sized + 'static,
197    {
198        RcBiConsumer::new(move |t, u| self.accept(t, u))
199    }
200
201    /// Converts to ArcBiConsumer
202    ///
203    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
204    /// calling this method.
205    ///
206    /// # Returns
207    ///
208    /// Returns the wrapped `ArcBiConsumer<T, U>`
209    fn into_arc(self) -> ArcBiConsumer<T, U>
210    where
211        Self: Sized + Send + Sync + 'static,
212    {
213        ArcBiConsumer::new(move |t, u| self.accept(t, u))
214    }
215
216    /// Converts non-mutating bi-consumer to a closure
217    ///
218    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
219    /// calling this method.
220    ///
221    /// Converts the non-mutating bi-consumer to a closure usable with standard
222    /// library methods requiring `Fn`.
223    ///
224    /// # Returns
225    ///
226    /// Returns a closure implementing `Fn(&T, &U)`
227    ///
228    /// # Examples
229    ///
230    /// ```rust
231    /// use qubit_function::{BiConsumer, BoxBiConsumer};
232    ///
233    /// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
234    ///     println!("Sum: {}", x + y);
235    /// });
236    /// let func = consumer.into_fn();
237    /// func(&5, &3);
238    /// ```
239    fn into_fn(self) -> impl Fn(&T, &U)
240    where
241        Self: Sized + 'static,
242    {
243        move |t, u| self.accept(t, u)
244    }
245
246    /// Convert to BiConsumerOnce
247    ///
248    /// **⚠️ Consumes `self`**: The original consumer will be unavailable after calling this method.
249    ///
250    /// Converts a reusable non-mutating bi-consumer to a one-time consumer that consumes itself on use.
251    /// This enables passing `BiConsumer` to functions that require `BiConsumerOnce`.
252    ///
253    /// # Returns
254    ///
255    /// Returns a `BoxBiConsumerOnce<T, U>`
256    fn into_once(self) -> BoxBiConsumerOnce<T, U>
257    where
258        Self: Sized + 'static,
259    {
260        BoxBiConsumerOnce::new(move |t, u| self.accept(t, u))
261    }
262
263    /// Converts to BoxBiConsumer (without consuming self)
264    ///
265    /// Creates a new `BoxBiConsumer` by cloning the current consumer.
266    /// The original consumer remains usable after this call.
267    ///
268    /// # Returns
269    ///
270    /// Returns a new `BoxBiConsumer<T, U>`
271    ///
272    /// # Examples
273    ///
274    /// ```rust
275    /// use qubit_function::{BiConsumer, ArcBiConsumer};
276    ///
277    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
278    ///     println!("Sum: {}", x + y);
279    /// });
280    /// let box_consumer = consumer.to_box();
281    /// box_consumer.accept(&5, &3);
282    /// // Original consumer still usable
283    /// consumer.accept(&10, &20);
284    /// ```
285    fn to_box(&self) -> BoxBiConsumer<T, U>
286    where
287        Self: Clone + 'static,
288    {
289        self.clone().into_box()
290    }
291
292    /// Converts to RcBiConsumer (without consuming self)
293    ///
294    /// Creates a new `RcBiConsumer` by cloning the current consumer.
295    /// The original consumer remains usable after this call.
296    ///
297    /// # Returns
298    ///
299    /// Returns a new `RcBiConsumer<T, U>`
300    ///
301    /// # Examples
302    ///
303    /// ```rust
304    /// use qubit_function::{BiConsumer, ArcBiConsumer};
305    ///
306    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
307    ///     println!("Sum: {}", x + y);
308    /// });
309    /// let rc_consumer = consumer.to_rc();
310    /// rc_consumer.accept(&5, &3);
311    /// // Original consumer still usable
312    /// consumer.accept(&10, &20);
313    /// ```
314    fn to_rc(&self) -> RcBiConsumer<T, U>
315    where
316        Self: Clone + 'static,
317    {
318        self.clone().into_rc()
319    }
320
321    /// Converts to ArcBiConsumer (without consuming self)
322    ///
323    /// Creates a new `ArcBiConsumer` by cloning the current consumer.
324    /// The original consumer remains usable after this call.
325    ///
326    /// # Returns
327    ///
328    /// Returns a new `ArcBiConsumer<T, U>`
329    ///
330    /// # Examples
331    ///
332    /// ```rust
333    /// use qubit_function::{BiConsumer, ArcBiConsumer};
334    ///
335    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
336    ///     println!("Sum: {}", x + y);
337    /// });
338    /// let arc_consumer = consumer.to_arc();
339    /// arc_consumer.accept(&5, &3);
340    /// ```
341    fn to_arc(&self) -> ArcBiConsumer<T, U>
342    where
343        Self: Clone + Send + Sync + 'static,
344    {
345        self.clone().into_arc()
346    }
347
348    /// Converts to a closure (without consuming self)
349    ///
350    /// Creates a new closure by cloning the current consumer.
351    /// The original consumer remains usable after this call.
352    ///
353    /// # Returns
354    ///
355    /// Returns a closure implementing `Fn(&T, &U)`
356    ///
357    /// # Examples
358    ///
359    /// ```rust
360    /// use qubit_function::{BiConsumer, RcBiConsumer};
361    ///
362    /// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
363    ///     println!("Sum: {}", x + y);
364    /// });
365    /// let func = consumer.to_fn();
366    /// func(&5, &3);
367    /// // Original consumer still usable
368    /// consumer.accept(&10, &20);
369    /// ```
370    fn to_fn(&self) -> impl Fn(&T, &U)
371    where
372        Self: Clone + 'static,
373    {
374        self.clone().into_fn()
375    }
376
377    /// Convert to BiConsumerOnce without consuming self
378    ///
379    /// **⚠️ Requires Clone**: This method requires `Self` to implement `Clone`.
380    /// Clones the current consumer and converts the clone to a one-time consumer.
381    ///
382    /// # Returns
383    ///
384    /// Returns a `BoxBiConsumerOnce<T, U>`
385    fn to_once(&self) -> BoxBiConsumerOnce<T, U>
386    where
387        Self: Clone + 'static,
388    {
389        self.clone().into_once()
390    }
391}