Skip to main content

qubit_function/consumers/
bi_consumer.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! # BiConsumer Types
11//!
12//! Provides non-mutating bi-consumer interface implementations for operations
13//! that accept two input parameters without modifying their own state or
14//! the input values.
15//!
16//! It is similar to the `Fn(&T, &U)` trait in the standard library.
17//!
18//! This module provides a unified `BiConsumer` trait and three
19//! concrete implementations based on different ownership models:
20//!
21//! - **`BoxBiConsumer<T, U>`**: Box-based single ownership
22//! - **`ArcBiConsumer<T, U>`**: Arc-based thread-safe shared
23//!   ownership
24//! - **`RcBiConsumer<T, U>`**: Rc-based single-threaded shared
25//!   ownership
26//!
27//! # Design Philosophy
28//!
29//! BiConsumer uses `Fn(&T, &U)` semantics: it is invoked through `&self` and
30//! receives shared references to both input values.
31//!
32//! Suitable for pure observation, logging, and notification scenarios with two
33//! parameters. Compared to `StatefulBiConsumer`, `BiConsumer` does not require
34//! wrapper-level interior mutability (`Mutex`/`RefCell`), making it more
35//! efficient and easier to share.
36//!
37use std::rc::Rc;
38use std::sync::Arc;
39
40use crate::consumers::{
41    bi_consumer_once::BoxBiConsumerOnce,
42    macros::{
43        impl_box_conditional_consumer,
44        impl_box_consumer_methods,
45        impl_conditional_consumer_clone,
46        impl_conditional_consumer_conversions,
47        impl_conditional_consumer_debug_display,
48        impl_consumer_clone,
49        impl_consumer_common_methods,
50        impl_consumer_debug_display,
51        impl_shared_conditional_consumer,
52        impl_shared_consumer_methods,
53    },
54};
55use crate::macros::{
56    impl_arc_conversions,
57    impl_box_conversions,
58    impl_closure_trait,
59    impl_rc_conversions,
60};
61use crate::predicates::bi_predicate::{
62    ArcBiPredicate,
63    BiPredicate,
64    BoxBiPredicate,
65    RcBiPredicate,
66};
67
68// ==========================================================================
69// Type Aliases
70// ==========================================================================
71
72/// Type alias for non-mutating bi-consumer function signature.
73type BiConsumerFn<T, U> = dyn Fn(&T, &U);
74
75/// Type alias for thread-safe non-mutating bi-consumer function signature.
76type ThreadSafeBiConsumerFn<T, U> = dyn Fn(&T, &U) + Send + Sync;
77
78mod box_bi_consumer;
79pub use box_bi_consumer::BoxBiConsumer;
80mod rc_bi_consumer;
81pub use rc_bi_consumer::RcBiConsumer;
82mod arc_bi_consumer;
83pub use arc_bi_consumer::ArcBiConsumer;
84mod fn_bi_consumer_ops;
85pub use fn_bi_consumer_ops::FnBiConsumerOps;
86mod box_conditional_bi_consumer;
87pub use box_conditional_bi_consumer::BoxConditionalBiConsumer;
88mod arc_conditional_bi_consumer;
89pub use arc_conditional_bi_consumer::ArcConditionalBiConsumer;
90mod rc_conditional_bi_consumer;
91pub use rc_conditional_bi_consumer::RcConditionalBiConsumer;
92
93// =======================================================================
94// 1. BiConsumer Trait - Unified Interface
95// =======================================================================
96
97/// BiConsumer trait - Unified non-mutating bi-consumer interface
98///
99/// It is similar to the `Fn(&T, &U)` trait in the standard library.
100///
101/// Defines core behavior for all non-mutating bi-consumer types. The API uses
102/// `&self` and shared input references, so callers can use a bi-consumer
103/// without granting mutable access to the consumer wrapper or input values.
104///
105/// # Automatic Implementations
106///
107/// - All closures implementing `Fn(&T, &U)`
108/// - `BoxBiConsumer<T, U>`, `ArcBiConsumer<T, U>`,
109///   `RcBiConsumer<T, U>`
110///
111/// # Features
112///
113/// - **Unified Interface**: All non-mutating bi-consumer types share the same
114///   `accept` method signature
115/// - **Automatic Implementation**: Closures automatically implement this
116///   trait with zero overhead
117/// - **Type Conversions**: Easy conversion between ownership models
118/// - **Generic Programming**: Write functions accepting any non-mutating
119///   bi-consumer type
120/// - **No Wrapper Interior Mutability**: No need for Mutex or RefCell in the
121///   wrapper, making shared ownership more efficient
122///
123/// # Examples
124///
125/// ```rust
126/// use qubit_function::{BiConsumer, BoxBiConsumer};
127///
128/// fn apply_consumer<C: BiConsumer<i32, i32>>(
129///     consumer: &C,
130///     a: &i32,
131///     b: &i32
132/// ) {
133///     consumer.accept(a, b);
134/// }
135///
136/// let box_con = BoxBiConsumer::new(|x: &i32, y: &i32| {
137///     println!("Sum: {}", x + y);
138/// });
139/// apply_consumer(&box_con, &5, &3);
140/// ```
141///
142pub trait BiConsumer<T, U> {
143    /// Performs the non-mutating consumption operation
144    ///
145    /// Executes an operation on the given two references. The operation
146    /// typically reads input values or produces side effects, but neither
147    /// modifies the input values nor the consumer's own state.
148    ///
149    /// # Parameters
150    ///
151    /// * `first` - Reference to the first value to consume
152    /// * `second` - Reference to the second value to consume
153    ///
154    /// # Examples
155    ///
156    /// ```rust
157    /// use qubit_function::{BiConsumer, BoxBiConsumer};
158    ///
159    /// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
160    ///     println!("Values: {}, {}", x, y);
161    /// });
162    /// consumer.accept(&5, &3);
163    /// ```
164    fn accept(&self, first: &T, second: &U);
165
166    /// Converts to BoxBiConsumer
167    ///
168    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
169    /// calling this method.
170    ///
171    /// # Returns
172    ///
173    /// Returns the wrapped `BoxBiConsumer<T, U>`
174    fn into_box(self) -> BoxBiConsumer<T, U>
175    where
176        Self: Sized + 'static,
177    {
178        BoxBiConsumer::new(move |t, u| self.accept(t, u))
179    }
180
181    /// Converts to RcBiConsumer
182    ///
183    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
184    /// calling this method.
185    ///
186    /// # Returns
187    ///
188    /// Returns the wrapped `RcBiConsumer<T, U>`
189    fn into_rc(self) -> RcBiConsumer<T, U>
190    where
191        Self: Sized + 'static,
192    {
193        RcBiConsumer::new(move |t, u| self.accept(t, u))
194    }
195
196    /// Converts to ArcBiConsumer
197    ///
198    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
199    /// calling this method.
200    ///
201    /// # Returns
202    ///
203    /// Returns the wrapped `ArcBiConsumer<T, U>`
204    fn into_arc(self) -> ArcBiConsumer<T, U>
205    where
206        Self: Sized + Send + Sync + 'static,
207    {
208        ArcBiConsumer::new(move |t, u| self.accept(t, u))
209    }
210
211    /// Converts non-mutating bi-consumer to a closure
212    ///
213    /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
214    /// calling this method.
215    ///
216    /// Converts the non-mutating bi-consumer to a closure usable with standard
217    /// library methods requiring `Fn`.
218    ///
219    /// # Returns
220    ///
221    /// Returns a closure implementing `Fn(&T, &U)`
222    ///
223    /// # Examples
224    ///
225    /// ```rust
226    /// use qubit_function::{BiConsumer, BoxBiConsumer};
227    ///
228    /// let consumer = BoxBiConsumer::new(|x: &i32, y: &i32| {
229    ///     println!("Sum: {}", x + y);
230    /// });
231    /// let func = consumer.into_fn();
232    /// func(&5, &3);
233    /// ```
234    fn into_fn(self) -> impl Fn(&T, &U)
235    where
236        Self: Sized + 'static,
237    {
238        move |t, u| self.accept(t, u)
239    }
240
241    /// Convert to BiConsumerOnce
242    ///
243    /// **⚠️ Consumes `self`**: The original consumer will be unavailable after calling this method.
244    ///
245    /// Converts a reusable non-mutating bi-consumer to a one-time consumer that consumes itself on use.
246    /// This enables passing `BiConsumer` to functions that require `BiConsumerOnce`.
247    ///
248    /// # Returns
249    ///
250    /// Returns a `BoxBiConsumerOnce<T, U>`
251    fn into_once(self) -> BoxBiConsumerOnce<T, U>
252    where
253        Self: Sized + 'static,
254    {
255        BoxBiConsumerOnce::new(move |t, u| self.accept(t, u))
256    }
257
258    /// Converts to BoxBiConsumer (without consuming self)
259    ///
260    /// Creates a new `BoxBiConsumer` by cloning the current consumer.
261    /// The original consumer remains usable after this call.
262    ///
263    /// # Returns
264    ///
265    /// Returns a new `BoxBiConsumer<T, U>`
266    ///
267    /// # Examples
268    ///
269    /// ```rust
270    /// use qubit_function::{BiConsumer, ArcBiConsumer};
271    ///
272    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
273    ///     println!("Sum: {}", x + y);
274    /// });
275    /// let box_consumer = consumer.to_box();
276    /// box_consumer.accept(&5, &3);
277    /// // Original consumer still usable
278    /// consumer.accept(&10, &20);
279    /// ```
280    fn to_box(&self) -> BoxBiConsumer<T, U>
281    where
282        Self: Clone + 'static,
283    {
284        self.clone().into_box()
285    }
286
287    /// Converts to RcBiConsumer (without consuming self)
288    ///
289    /// Creates a new `RcBiConsumer` by cloning the current consumer.
290    /// The original consumer remains usable after this call.
291    ///
292    /// # Returns
293    ///
294    /// Returns a new `RcBiConsumer<T, U>`
295    ///
296    /// # Examples
297    ///
298    /// ```rust
299    /// use qubit_function::{BiConsumer, ArcBiConsumer};
300    ///
301    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
302    ///     println!("Sum: {}", x + y);
303    /// });
304    /// let rc_consumer = consumer.to_rc();
305    /// rc_consumer.accept(&5, &3);
306    /// // Original consumer still usable
307    /// consumer.accept(&10, &20);
308    /// ```
309    fn to_rc(&self) -> RcBiConsumer<T, U>
310    where
311        Self: Clone + 'static,
312    {
313        self.clone().into_rc()
314    }
315
316    /// Converts to ArcBiConsumer (without consuming self)
317    ///
318    /// Creates a new `ArcBiConsumer` by cloning the current consumer.
319    /// The original consumer remains usable after this call.
320    ///
321    /// # Returns
322    ///
323    /// Returns a new `ArcBiConsumer<T, U>`
324    ///
325    /// # Examples
326    ///
327    /// ```rust
328    /// use qubit_function::{BiConsumer, ArcBiConsumer};
329    ///
330    /// let consumer = ArcBiConsumer::new(|x: &i32, y: &i32| {
331    ///     println!("Sum: {}", x + y);
332    /// });
333    /// let arc_consumer = consumer.to_arc();
334    /// arc_consumer.accept(&5, &3);
335    /// ```
336    fn to_arc(&self) -> ArcBiConsumer<T, U>
337    where
338        Self: Clone + Send + Sync + 'static,
339    {
340        self.clone().into_arc()
341    }
342
343    /// Converts to a closure (without consuming self)
344    ///
345    /// Creates a new closure by cloning the current consumer.
346    /// The original consumer remains usable after this call.
347    ///
348    /// # Returns
349    ///
350    /// Returns a closure implementing `Fn(&T, &U)`
351    ///
352    /// # Examples
353    ///
354    /// ```rust
355    /// use qubit_function::{BiConsumer, RcBiConsumer};
356    ///
357    /// let consumer = RcBiConsumer::new(|x: &i32, y: &i32| {
358    ///     println!("Sum: {}", x + y);
359    /// });
360    /// let func = consumer.to_fn();
361    /// func(&5, &3);
362    /// // Original consumer still usable
363    /// consumer.accept(&10, &20);
364    /// ```
365    fn to_fn(&self) -> impl Fn(&T, &U)
366    where
367        Self: Clone + 'static,
368    {
369        self.clone().into_fn()
370    }
371
372    /// Convert to BiConsumerOnce without consuming self
373    ///
374    /// **⚠️ Requires Clone**: This method requires `Self` to implement `Clone`.
375    /// Clones the current consumer and converts the clone to a one-time consumer.
376    ///
377    /// # Returns
378    ///
379    /// Returns a `BoxBiConsumerOnce<T, U>`
380    fn to_once(&self) -> BoxBiConsumerOnce<T, U>
381    where
382        Self: Clone + 'static,
383    {
384        self.clone().into_once()
385    }
386}