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}