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}