prism3_function/bi_consumer_once.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025.
4 * 3-Prism Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # BiConsumerOnce Types
10//!
11//! Provides one-time bi-consumer interface implementations for operations
12//! accepting two input parameters without returning a result.
13//!
14//! This module provides a unified `BiConsumerOnce` trait and one concrete
15//! implementation:
16//!
17//! - **`BoxBiConsumerOnce<T, U>`**: Box-based single ownership
18//! implementation
19//!
20//! # Why No Arc/Rc Variants?
21//!
22//! Unlike `BiConsumer` and `ReadonlyBiConsumer`, this module does **not**
23//! provide `ArcBiConsumerOnce` or `RcBiConsumerOnce` implementations. This
24//! is a design decision based on the fundamental incompatibility between
25//! `FnOnce` semantics and shared ownership. See the design documentation
26//! for details.
27//!
28//! # Design Philosophy
29//!
30//! BiConsumerOnce uses `FnOnce(&T, &U)` semantics: for truly one-time
31//! consumption operations. Unlike BiConsumer, BiConsumerOnce consumes
32//! itself on first call. Suitable for initialization callbacks, cleanup
33//! callbacks, etc.
34//!
35//! # Author
36//!
37//! Haixing Hu
38
39use std::fmt;
40
41use crate::bi_predicate::{BiPredicate, BoxBiPredicate};
42
43// ==========================================================================
44// Type Aliases
45// ==========================================================================
46
47/// Type alias for bi-consumer once function signature.
48type BiConsumerOnceFn<T, U> = dyn FnOnce(&T, &U);
49
50// =======================================================================
51// 1. BiConsumerOnce Trait - Unified Interface
52// =======================================================================
53
54/// BiConsumerOnce trait - Unified one-time bi-consumer interface
55///
56/// Defines core behavior for all one-time bi-consumer types. Similar to a
57/// bi-consumer implementing `FnOnce(&T, &U)`, performs operations
58/// accepting two value references but returning no result (side effects
59/// only), consuming itself in the process.
60///
61/// # Automatic Implementations
62///
63/// - All closures implementing `FnOnce(&T, &U)`
64/// - `BoxBiConsumerOnce<T, U>`
65///
66/// # Features
67///
68/// - **Unified Interface**: All bi-consumer types share the same `accept`
69/// method signature
70/// - **Automatic Implementation**: Closures automatically implement this
71/// trait with zero overhead
72/// - **Type Conversions**: Can convert to BoxBiConsumerOnce
73/// - **Generic Programming**: Write functions accepting any one-time
74/// bi-consumer type
75///
76/// # Examples
77///
78/// ```rust
79/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
80/// use std::sync::{Arc, Mutex};
81///
82/// fn apply_consumer<C: BiConsumerOnce<i32, i32>>(
83/// consumer: C,
84/// a: &i32,
85/// b: &i32
86/// ) {
87/// consumer.accept(a, b);
88/// }
89///
90/// let log = Arc::new(Mutex::new(Vec::new()));
91/// let l = log.clone();
92/// let box_con = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
93/// l.lock().unwrap().push(*x + *y);
94/// });
95/// apply_consumer(box_con, &5, &3);
96/// assert_eq!(*log.lock().unwrap(), vec![8]);
97/// ```
98///
99/// # Author
100///
101/// Haixing Hu
102pub trait BiConsumerOnce<T, U> {
103 /// Performs the one-time consumption operation
104 ///
105 /// Executes an operation on the given two references. The operation
106 /// typically reads input values or produces side effects, but does not
107 /// modify the input values themselves. Consumes self.
108 ///
109 /// # Parameters
110 ///
111 /// * `first` - Reference to the first value to consume
112 /// * `second` - Reference to the second value to consume
113 ///
114 /// # Examples
115 ///
116 /// ```rust
117 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
118 ///
119 /// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
120 /// println!("Sum: {}", x + y);
121 /// });
122 /// consumer.accept(&5, &3);
123 /// ```
124 fn accept(self, first: &T, second: &U);
125
126 /// Converts to BoxBiConsumerOnce
127 ///
128 /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
129 /// calling this method.
130 ///
131 /// # Returns
132 ///
133 /// Returns the wrapped `BoxBiConsumerOnce<T, U>`
134 ///
135 /// # Examples
136 ///
137 /// ```rust
138 /// use prism3_function::BiConsumerOnce;
139 /// use std::sync::{Arc, Mutex};
140 ///
141 /// let log = Arc::new(Mutex::new(Vec::new()));
142 /// let l = log.clone();
143 /// let closure = move |x: &i32, y: &i32| {
144 /// l.lock().unwrap().push(*x + *y);
145 /// };
146 /// let box_consumer = closure.into_box();
147 /// box_consumer.accept(&5, &3);
148 /// assert_eq!(*log.lock().unwrap(), vec![8]);
149 /// ```
150 fn into_box(self) -> BoxBiConsumerOnce<T, U>
151 where
152 Self: Sized + 'static,
153 T: 'static,
154 U: 'static;
155
156 /// Converts to a closure
157 ///
158 /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
159 /// calling this method.
160 ///
161 /// Converts the one-time bi-consumer to a closure usable with standard
162 /// library methods requiring `FnOnce`.
163 ///
164 /// # Returns
165 ///
166 /// Returns a closure implementing `FnOnce(&T, &U)`
167 fn into_fn(self) -> impl FnOnce(&T, &U)
168 where
169 Self: Sized + 'static,
170 T: 'static,
171 U: 'static;
172}
173
174// =======================================================================
175// 2. BoxBiConsumerOnce - Single Ownership Implementation
176// =======================================================================
177
178/// BoxBiConsumerOnce struct
179///
180/// A one-time bi-consumer implementation based on
181/// `Box<dyn FnOnce(&T, &U)>` for single ownership scenarios. This is the
182/// simplest one-time bi-consumer type for truly one-time use.
183///
184/// # Features
185///
186/// - **Single Ownership**: Not cloneable, ownership moves on use
187/// - **Zero Overhead**: No reference counting or locking
188/// - **One-Time Use**: Consumes self on first call
189/// - **Builder Pattern**: Method chaining consumes `self` naturally
190///
191/// # Use Cases
192///
193/// Choose `BoxBiConsumerOnce` when:
194/// - The bi-consumer is truly used only once
195/// - Building pipelines where ownership naturally flows
196/// - The consumer captures values that should be consumed
197/// - Performance is critical and sharing overhead is unacceptable
198///
199/// # Performance
200///
201/// `BoxBiConsumerOnce` has the best performance:
202/// - No reference counting overhead
203/// - No lock acquisition or runtime borrow checking
204/// - Direct function call through vtable
205/// - Minimal memory footprint (single pointer)
206///
207/// # Examples
208///
209/// ```rust
210/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
211///
212/// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
213/// println!("Sum: {}", x + y);
214/// });
215/// consumer.accept(&5, &3);
216/// ```
217///
218/// # Author
219///
220/// Haixing Hu
221pub struct BoxBiConsumerOnce<T, U> {
222 function: Box<BiConsumerOnceFn<T, U>>,
223 name: Option<String>,
224}
225
226impl<T, U> BoxBiConsumerOnce<T, U>
227where
228 T: 'static,
229 U: 'static,
230{
231 /// Creates a new BoxBiConsumerOnce
232 ///
233 /// # Type Parameters
234 ///
235 /// * `F` - The closure type
236 ///
237 /// # Parameters
238 ///
239 /// * `f` - The closure to wrap
240 ///
241 /// # Returns
242 ///
243 /// Returns a new `BoxBiConsumerOnce<T, U>` instance
244 ///
245 /// # Examples
246 ///
247 /// ```rust
248 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
249 /// use std::sync::{Arc, Mutex};
250 ///
251 /// let log = Arc::new(Mutex::new(Vec::new()));
252 /// let l = log.clone();
253 /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
254 /// l.lock().unwrap().push(*x * 2 + *y);
255 /// });
256 /// consumer.accept(&5, &3);
257 /// assert_eq!(*log.lock().unwrap(), vec![13]);
258 /// ```
259 pub fn new<F>(f: F) -> Self
260 where
261 F: FnOnce(&T, &U) + 'static,
262 {
263 BoxBiConsumerOnce {
264 function: Box::new(f),
265 name: None,
266 }
267 }
268
269 /// Creates a new BoxBiConsumerOnce with a name
270 ///
271 /// # Type Parameters
272 ///
273 /// * `F` - The closure type
274 ///
275 /// # Parameters
276 ///
277 /// * `name` - The name of the consumer
278 /// * `f` - The closure to wrap
279 ///
280 /// # Returns
281 ///
282 /// Returns a new `BoxBiConsumerOnce<T, U>` instance with the specified name
283 ///
284 /// # Examples
285 ///
286 /// ```rust
287 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
288 /// use std::sync::{Arc, Mutex};
289 ///
290 /// let log = Arc::new(Mutex::new(Vec::new()));
291 /// let l = log.clone();
292 /// let consumer = BoxBiConsumerOnce::new_with_name("sum_logger", move |x: &i32, y: &i32| {
293 /// l.lock().unwrap().push(*x + *y);
294 /// });
295 /// assert_eq!(consumer.name(), Some("sum_logger"));
296 /// consumer.accept(&5, &3);
297 /// assert_eq!(*log.lock().unwrap(), vec![8]);
298 /// ```
299 pub fn new_with_name<F>(name: &str, f: F) -> Self
300 where
301 F: FnOnce(&T, &U) + 'static,
302 {
303 BoxBiConsumerOnce {
304 function: Box::new(f),
305 name: Some(name.to_string()),
306 }
307 }
308
309 /// Gets the name of the consumer
310 pub fn name(&self) -> Option<&str> {
311 self.name.as_deref()
312 }
313
314 /// Sets the name of the consumer
315 pub fn set_name(&mut self, name: impl Into<String>) {
316 self.name = Some(name.into());
317 }
318
319 /// Creates a no-op bi-consumer
320 ///
321 /// # Returns
322 ///
323 /// Returns a no-op bi-consumer
324 ///
325 /// # Examples
326 ///
327 /// ```rust
328 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
329 ///
330 /// let noop = BoxBiConsumerOnce::<i32, i32>::noop();
331 /// noop.accept(&42, &10);
332 /// // Values unchanged
333 /// ```
334 pub fn noop() -> Self {
335 BoxBiConsumerOnce::new(|_, _| {})
336 }
337
338 /// Chains another one-time bi-consumer in sequence
339 ///
340 /// Returns a new consumer executing the current operation first, then
341 /// the next operation. Consumes self.
342 ///
343 /// # Type Parameters
344 ///
345 /// * `C` - The type of the next consumer
346 ///
347 /// # Parameters
348 ///
349 /// * `next` - The consumer to execute after the current operation. **Note:
350 /// This parameter is passed by value and will transfer ownership.** Since
351 /// `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
352 /// Can be:
353 /// - A closure: `|x: &T, y: &U|`
354 /// - A `BoxBiConsumerOnce<T, U>`
355 /// - Any type implementing `BiConsumerOnce<T, U>`
356 ///
357 /// # Returns
358 ///
359 /// Returns a new composed `BoxBiConsumerOnce<T, U>`
360 ///
361 /// # Examples
362 ///
363 /// ```rust
364 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
365 /// use std::sync::{Arc, Mutex};
366 ///
367 /// let log = Arc::new(Mutex::new(Vec::new()));
368 /// let l1 = log.clone();
369 /// let l2 = log.clone();
370 /// let first = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
371 /// l1.lock().unwrap().push(*x + *y);
372 /// });
373 /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
374 /// l2.lock().unwrap().push(*x * *y);
375 /// });
376 ///
377 /// // Both first and second are moved and consumed
378 /// let chained = first.and_then(second);
379 /// chained.accept(&5, &3);
380 /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
381 /// // first.accept(&2, &3); // Would not compile - moved
382 /// // second.accept(&2, &3); // Would not compile - moved
383 /// ```
384 pub fn and_then<C>(self, next: C) -> Self
385 where
386 C: BiConsumerOnce<T, U> + 'static,
387 {
388 let first = self.function;
389 let second = next;
390 BoxBiConsumerOnce::new(move |t, u| {
391 first(t, u);
392 second.accept(t, u);
393 })
394 }
395
396 /// Creates a conditional bi-consumer
397 ///
398 /// Returns a bi-consumer that only executes when a predicate is satisfied.
399 ///
400 /// # Type Parameters
401 ///
402 /// * `P` - The predicate type
403 ///
404 /// # Parameters
405 ///
406 /// * `predicate` - The condition to check. **Note: This parameter is passed
407 /// by value and will transfer ownership.** If you need to preserve the
408 /// original bi-predicate, clone it first (if it implements `Clone`). Can be:
409 /// - A closure: `|x: &T, y: &U| -> bool`
410 /// - A function pointer: `fn(&T, &U) -> bool`
411 /// - A `BoxBiPredicate<T, U>`
412 /// - Any type implementing `BiPredicate<T, U>`
413 ///
414 /// # Returns
415 ///
416 /// Returns `BoxConditionalBiConsumerOnce<T, U>`
417 ///
418 /// # Examples
419 ///
420 /// ```rust
421 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
422 /// use std::sync::{Arc, Mutex};
423 ///
424 /// let log = Arc::new(Mutex::new(Vec::new()));
425 /// let l = log.clone();
426 /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
427 /// l.lock().unwrap().push(*x + *y);
428 /// });
429 /// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
430 ///
431 /// conditional.accept(&5, &3);
432 /// assert_eq!(*log.lock().unwrap(), vec![8]);
433 /// ```
434 pub fn when<P>(self, predicate: P) -> BoxConditionalBiConsumerOnce<T, U>
435 where
436 P: BiPredicate<T, U> + 'static,
437 {
438 BoxConditionalBiConsumerOnce {
439 consumer: self,
440 predicate: predicate.into_box(),
441 }
442 }
443}
444
445impl<T, U> BiConsumerOnce<T, U> for BoxBiConsumerOnce<T, U> {
446 fn accept(self, first: &T, second: &U) {
447 (self.function)(first, second)
448 }
449
450 fn into_box(self) -> BoxBiConsumerOnce<T, U>
451 where
452 T: 'static,
453 U: 'static,
454 {
455 self
456 }
457
458 fn into_fn(self) -> impl FnOnce(&T, &U)
459 where
460 T: 'static,
461 U: 'static,
462 {
463 self.function
464 }
465}
466
467impl<T, U> fmt::Debug for BoxBiConsumerOnce<T, U> {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 f.debug_struct("BoxBiConsumerOnce")
470 .field("name", &self.name)
471 .field("function", &"<function>")
472 .finish()
473 }
474}
475
476impl<T, U> fmt::Display for BoxBiConsumerOnce<T, U> {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 match &self.name {
479 Some(name) => write!(f, "BoxBiConsumerOnce({})", name),
480 None => write!(f, "BoxBiConsumerOnce"),
481 }
482 }
483}
484
485// =======================================================================
486// 3. BoxConditionalBiConsumerOnce - Box-based Conditional BiConsumerOnce
487// =======================================================================
488
489/// BoxConditionalBiConsumerOnce struct
490///
491/// A conditional one-time bi-consumer that only executes when a predicate is satisfied.
492/// Uses `BoxBiConsumerOnce` and `BoxBiPredicate` for single ownership semantics.
493///
494/// This type is typically created by calling `BoxBiConsumerOnce::when()` and is
495/// designed to work with the `or_else()` method to create if-then-else logic.
496///
497/// # Features
498///
499/// - **Single Ownership**: Not cloneable, consumes `self` on use
500/// - **Conditional Execution**: Only consumes when predicate returns `true`
501/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
502/// - **Implements BiConsumerOnce**: Can be used anywhere a `BiConsumerOnce` is expected
503///
504/// # Examples
505///
506/// ## Basic Conditional Execution
507///
508/// ```rust
509/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
510/// use std::sync::{Arc, Mutex};
511///
512/// let log = Arc::new(Mutex::new(Vec::new()));
513/// let l = log.clone();
514/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
515/// l.lock().unwrap().push(*x + *y);
516/// });
517/// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
518///
519/// conditional.accept(&5, &3);
520/// assert_eq!(*log.lock().unwrap(), vec![8]); // Executed
521/// ```
522///
523/// ## With or_else Branch
524///
525/// ```rust
526/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
527/// use std::sync::{Arc, Mutex};
528///
529/// let log = Arc::new(Mutex::new(Vec::new()));
530/// let l1 = log.clone();
531/// let l2 = log.clone();
532/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
533/// l1.lock().unwrap().push(*x + *y);
534/// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
535/// .or_else(move |x: &i32, y: &i32| {
536/// l2.lock().unwrap().push(*x * *y);
537/// });
538///
539/// consumer.accept(&5, &3);
540/// assert_eq!(*log.lock().unwrap(), vec![8]); // when branch executed
541/// ```
542///
543/// # Author
544///
545/// Haixing Hu
546pub struct BoxConditionalBiConsumerOnce<T, U> {
547 consumer: BoxBiConsumerOnce<T, U>,
548 predicate: BoxBiPredicate<T, U>,
549}
550
551impl<T, U> BiConsumerOnce<T, U> for BoxConditionalBiConsumerOnce<T, U>
552where
553 T: 'static,
554 U: 'static,
555{
556 fn accept(self, first: &T, second: &U) {
557 if self.predicate.test(first, second) {
558 self.consumer.accept(first, second);
559 }
560 }
561
562 fn into_box(self) -> BoxBiConsumerOnce<T, U> {
563 let pred = self.predicate;
564 let consumer = self.consumer;
565 BoxBiConsumerOnce::new(move |t, u| {
566 if pred.test(t, u) {
567 consumer.accept(t, u);
568 }
569 })
570 }
571
572 fn into_fn(self) -> impl FnOnce(&T, &U) {
573 let pred = self.predicate;
574 let consumer = self.consumer;
575 move |t: &T, u: &U| {
576 if pred.test(t, u) {
577 consumer.accept(t, u);
578 }
579 }
580 }
581}
582
583impl<T, U> BoxConditionalBiConsumerOnce<T, U>
584where
585 T: 'static,
586 U: 'static,
587{
588 /// Chains another consumer in sequence
589 ///
590 /// Combines the current conditional consumer with another consumer into a new
591 /// consumer. The current conditional consumer executes first, followed by the
592 /// next consumer.
593 ///
594 /// # Parameters
595 ///
596 /// * `next` - The next consumer to execute. **Note: This parameter is passed
597 /// by value and will transfer ownership.** Since `BoxBiConsumerOnce`
598 /// cannot be cloned, the parameter will be consumed. Can be:
599 /// - A closure: `|x: &T, y: &U|`
600 /// - A `BoxBiConsumerOnce<T, U>`
601 /// - Any type implementing `BiConsumerOnce<T, U>`
602 ///
603 /// # Returns
604 ///
605 /// Returns a new `BoxBiConsumerOnce<T, U>`
606 ///
607 /// # Examples
608 ///
609 /// ```rust
610 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
611 /// use std::sync::{Arc, Mutex};
612 ///
613 /// let log = Arc::new(Mutex::new(Vec::new()));
614 /// let l1 = log.clone();
615 /// let l2 = log.clone();
616 /// let cond = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
617 /// l1.lock().unwrap().push(*x + *y);
618 /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0);
619 /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
620 /// l2.lock().unwrap().push(*x * *y);
621 /// });
622 ///
623 /// // Both cond and second are moved and consumed
624 /// let chained = cond.and_then(second);
625 /// chained.accept(&5, &3);
626 /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
627 /// // cond.accept(&2, &3); // Would not compile - moved
628 /// // second.accept(&2, &3); // Would not compile - moved
629 /// ```
630 pub fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
631 where
632 C: BiConsumerOnce<T, U> + 'static,
633 {
634 let first = self;
635 let second = next;
636 BoxBiConsumerOnce::new(move |t, u| {
637 first.accept(t, u);
638 second.accept(t, u);
639 })
640 }
641
642 /// Adds an else branch
643 ///
644 /// Executes the original consumer when the condition is satisfied, otherwise
645 /// executes else_consumer.
646 ///
647 /// # Parameters
648 ///
649 /// * `else_consumer` - The consumer for the else branch. **Note: This parameter
650 /// is passed by value and will transfer ownership.** Since
651 /// `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
652 /// Can be:
653 /// - A closure: `|x: &T, y: &U|`
654 /// - A `BoxBiConsumerOnce<T, U>`
655 /// - Any type implementing `BiConsumerOnce<T, U>`
656 ///
657 /// # Returns
658 ///
659 /// Returns the composed `BoxBiConsumerOnce<T, U>`
660 ///
661 /// # Examples
662 ///
663 /// ## Using a closure (recommended)
664 ///
665 /// ```rust
666 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
667 /// use std::sync::{Arc, Mutex};
668 ///
669 /// let log = Arc::new(Mutex::new(Vec::new()));
670 /// let l1 = log.clone();
671 /// let l2 = log.clone();
672 /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
673 /// l1.lock().unwrap().push(*x + *y);
674 /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
675 /// .or_else(move |x: &i32, y: &i32| {
676 /// l2.lock().unwrap().push(*x * *y);
677 /// });
678 ///
679 /// consumer.accept(&5, &3);
680 /// assert_eq!(*log.lock().unwrap(), vec![8]); // Condition satisfied
681 /// ```
682 pub fn or_else<C>(self, else_consumer: C) -> BoxBiConsumerOnce<T, U>
683 where
684 C: BiConsumerOnce<T, U> + 'static,
685 {
686 let pred = self.predicate;
687 let then_cons = self.consumer;
688 let else_cons = else_consumer;
689 BoxBiConsumerOnce::new(move |t, u| {
690 if pred.test(t, u) {
691 then_cons.accept(t, u);
692 } else {
693 else_cons.accept(t, u);
694 }
695 })
696 }
697}
698
699// =======================================================================
700// 5. Implement BiConsumerOnce trait for closures
701// =======================================================================
702
703/// Implements BiConsumerOnce for all FnOnce(&T, &U)
704impl<T, U, F> BiConsumerOnce<T, U> for F
705where
706 F: FnOnce(&T, &U),
707{
708 fn accept(self, first: &T, second: &U) {
709 self(first, second)
710 }
711
712 fn into_box(self) -> BoxBiConsumerOnce<T, U>
713 where
714 Self: Sized + 'static,
715 T: 'static,
716 U: 'static,
717 {
718 BoxBiConsumerOnce::new(self)
719 }
720
721 fn into_fn(self) -> impl FnOnce(&T, &U)
722 where
723 Self: Sized + 'static,
724 T: 'static,
725 U: 'static,
726 {
727 self
728 }
729}
730
731// =======================================================================
732// 6. Provide extension methods for closures
733// =======================================================================
734
735/// Extension trait providing one-time bi-consumer composition methods for
736/// closures
737///
738/// Provides `and_then` and other composition methods for all closures
739/// implementing `FnOnce(&T, &U)`, enabling direct method chaining on
740/// closures without explicit wrapper types.
741///
742/// # Features
743///
744/// - **Natural Syntax**: Chain operations directly on closures
745/// - **Returns BoxBiConsumerOnce**: Composition results can be further
746/// chained
747/// - **Zero Cost**: No overhead when composing closures
748/// - **Automatic Implementation**: All `FnOnce(&T, &U)` closures get
749/// these methods automatically
750///
751/// # Examples
752///
753/// ```rust
754/// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
755/// use std::sync::{Arc, Mutex};
756///
757/// let log = Arc::new(Mutex::new(Vec::new()));
758/// let l1 = log.clone();
759/// let l2 = log.clone();
760/// let chained = (move |x: &i32, y: &i32| {
761/// l1.lock().unwrap().push(*x + *y);
762/// }).and_then(move |x: &i32, y: &i32| {
763/// l2.lock().unwrap().push(*x * *y);
764/// });
765/// chained.accept(&5, &3);
766/// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
767/// ```
768///
769/// # Author
770///
771/// Haixing Hu
772pub trait FnBiConsumerOnceOps<T, U>: FnOnce(&T, &U) + Sized {
773 /// Chains another one-time bi-consumer in sequence
774 ///
775 /// Returns a new consumer executing the current operation first, then
776 /// the next operation. Consumes the current closure and returns
777 /// `BoxBiConsumerOnce<T, U>`.
778 ///
779 /// # Type Parameters
780 ///
781 /// * `C` - The type of the next consumer
782 ///
783 /// # Parameters
784 ///
785 /// * `next` - The consumer to execute after the current operation. **Note:
786 /// This parameter is passed by value and will transfer ownership.** Since
787 /// `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
788 /// Can be:
789 /// - A closure: `|x: &T, y: &U|`
790 /// - A `BoxBiConsumerOnce<T, U>`
791 /// - Any type implementing `BiConsumerOnce<T, U>`
792 ///
793 /// # Returns
794 ///
795 /// Returns the composed `BoxBiConsumerOnce<T, U>`
796 ///
797 /// # Examples
798 ///
799 /// ```rust
800 /// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
801 /// use std::sync::{Arc, Mutex};
802 ///
803 /// let log = Arc::new(Mutex::new(Vec::new()));
804 /// let l1 = log.clone();
805 /// let l2 = log.clone();
806 /// let chained = (move |x: &i32, y: &i32| {
807 /// l1.lock().unwrap().push(*x + *y);
808 /// }).and_then(move |x: &i32, y: &i32| {
809 /// l2.lock().unwrap().push(*x * *y);
810 /// }).and_then(|x: &i32, y: &i32| {
811 /// println!("Result: {}, {}", x, y);
812 /// });
813 ///
814 /// chained.accept(&5, &3);
815 /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
816 /// ```
817 fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
818 where
819 Self: 'static,
820 C: BiConsumerOnce<T, U> + 'static,
821 T: 'static,
822 U: 'static,
823 {
824 let first = self;
825 let second = next;
826 BoxBiConsumerOnce::new(move |t, u| {
827 first(t, u);
828 second.accept(t, u);
829 })
830 }
831}
832
833/// Implements FnBiConsumerOnceOps for all closure types
834impl<T, U, F> FnBiConsumerOnceOps<T, U> for F where F: FnOnce(&T, &U) {}