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_once(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_once(&5, &3);
123 /// ```
124 fn accept_once(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_once();
147 /// box_consumer.accept_once(&5, &3);
148 /// assert_eq!(*log.lock().unwrap(), vec![8]);
149 /// ```
150 fn into_box_once(self) -> BoxBiConsumerOnce<T, U>
151 where
152 Self: Sized + 'static,
153 T: 'static,
154 U: 'static,
155 {
156 BoxBiConsumerOnce::new(move |t, u| self.accept_once(t, u))
157 }
158
159 /// Converts to a closure
160 ///
161 /// **⚠️ Consumes `self`**: Original consumer becomes unavailable after
162 /// calling this method.
163 ///
164 /// Converts the one-time bi-consumer to a closure usable with standard
165 /// library methods requiring `FnOnce`.
166 ///
167 /// # Returns
168 ///
169 /// Returns a closure implementing `FnOnce(&T, &U)`
170 fn into_fn_once(self) -> impl FnOnce(&T, &U)
171 where
172 Self: Sized + 'static,
173 T: 'static,
174 U: 'static,
175 {
176 move |t, u| self.accept_once(t, u)
177 }
178
179 /// Convert to BoxBiConsumerOnce without consuming self
180 ///
181 /// **⚠️ Requires Clone**: This method requires `Self` to implement
182 /// `Clone`. Clones the current bi-consumer and then converts the clone
183 /// to a `BoxBiConsumerOnce`.
184 ///
185 /// # Returns
186 ///
187 /// Returns the wrapped `BoxBiConsumerOnce<T, U>`
188 ///
189 /// # Examples
190 ///
191 /// ```rust
192 /// use prism3_function::BiConsumerOnce;
193 /// use std::sync::{Arc, Mutex};
194 ///
195 /// let log = Arc::new(Mutex::new(Vec::new()));
196 /// let l = log.clone();
197 /// let closure = move |x: &i32, y: &i32| {
198 /// l.lock().unwrap().push(*x + *y);
199 /// };
200 /// let box_consumer = closure.to_box_once();
201 /// box_consumer.accept_once(&5, &3);
202 /// assert_eq!(*log.lock().unwrap(), vec![8]);
203 /// ```
204 fn to_box_once(&self) -> BoxBiConsumerOnce<T, U>
205 where
206 Self: Sized + Clone + 'static,
207 T: 'static,
208 U: 'static,
209 {
210 self.clone().into_box_once()
211 }
212
213 /// Convert to closure without consuming self
214 ///
215 /// **⚠️ Requires Clone**: This method requires `Self` to implement
216 /// `Clone`. Clones the current bi-consumer and then converts the clone
217 /// to a closure.
218 ///
219 /// # Returns
220 ///
221 /// Returns a closure implementing `FnOnce(&T, &U)`
222 ///
223 /// # Examples
224 ///
225 /// ```rust
226 /// use prism3_function::BiConsumerOnce;
227 /// use std::sync::{Arc, Mutex};
228 ///
229 /// let log = Arc::new(Mutex::new(Vec::new()));
230 /// let l = log.clone();
231 /// let closure = move |x: &i32, y: &i32| {
232 /// l.lock().unwrap().push(*x + *y);
233 /// };
234 /// let func = closure.to_fn_once();
235 /// func(&5, &3);
236 /// assert_eq!(*log.lock().unwrap(), vec![8]);
237 /// ```
238 fn to_fn_once(&self) -> impl FnOnce(&T, &U)
239 where
240 Self: Sized + Clone + 'static,
241 T: 'static,
242 U: 'static,
243 {
244 self.clone().into_fn_once()
245 }
246}
247
248// =======================================================================
249// 2. BoxBiConsumerOnce - Single Ownership Implementation
250// =======================================================================
251
252/// BoxBiConsumerOnce struct
253///
254/// A one-time bi-consumer implementation based on
255/// `Box<dyn FnOnce(&T, &U)>` for single ownership scenarios. This is the
256/// simplest one-time bi-consumer type for truly one-time use.
257///
258/// # Features
259///
260/// - **Single Ownership**: Not cloneable, ownership moves on use
261/// - **Zero Overhead**: No reference counting or locking
262/// - **One-Time Use**: Consumes self on first call
263/// - **Builder Pattern**: Method chaining consumes `self` naturally
264///
265/// # Use Cases
266///
267/// Choose `BoxBiConsumerOnce` when:
268/// - The bi-consumer is truly used only once
269/// - Building pipelines where ownership naturally flows
270/// - The consumer captures values that should be consumed
271/// - Performance is critical and sharing overhead is unacceptable
272///
273/// # Performance
274///
275/// `BoxBiConsumerOnce` has the best performance:
276/// - No reference counting overhead
277/// - No lock acquisition or runtime borrow checking
278/// - Direct function call through vtable
279/// - Minimal memory footprint (single pointer)
280///
281/// # Examples
282///
283/// ```rust
284/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
285///
286/// let consumer = BoxBiConsumerOnce::new(|x: &i32, y: &i32| {
287/// println!("Sum: {}", x + y);
288/// });
289/// consumer.accept_once(&5, &3);
290/// ```
291///
292/// # Author
293///
294/// Haixing Hu
295pub struct BoxBiConsumerOnce<T, U> {
296 function: Box<BiConsumerOnceFn<T, U>>,
297 name: Option<String>,
298}
299
300impl<T, U> BoxBiConsumerOnce<T, U>
301where
302 T: 'static,
303 U: 'static,
304{
305 /// Creates a new BoxBiConsumerOnce
306 ///
307 /// # Type Parameters
308 ///
309 /// * `F` - The closure type
310 ///
311 /// # Parameters
312 ///
313 /// * `f` - The closure to wrap
314 ///
315 /// # Returns
316 ///
317 /// Returns a new `BoxBiConsumerOnce<T, U>` instance
318 ///
319 /// # Examples
320 ///
321 /// ```rust
322 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
323 /// use std::sync::{Arc, Mutex};
324 ///
325 /// let log = Arc::new(Mutex::new(Vec::new()));
326 /// let l = log.clone();
327 /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
328 /// l.lock().unwrap().push(*x * 2 + *y);
329 /// });
330 /// consumer.accept_once(&5, &3);
331 /// assert_eq!(*log.lock().unwrap(), vec![13]);
332 /// ```
333 pub fn new<F>(f: F) -> Self
334 where
335 F: FnOnce(&T, &U) + 'static,
336 {
337 BoxBiConsumerOnce {
338 function: Box::new(f),
339 name: None,
340 }
341 }
342
343 /// Creates a new BoxBiConsumerOnce with a name
344 ///
345 /// # Type Parameters
346 ///
347 /// * `F` - The closure type
348 ///
349 /// # Parameters
350 ///
351 /// * `name` - The name of the consumer
352 /// * `f` - The closure to wrap
353 ///
354 /// # Returns
355 ///
356 /// Returns a new `BoxBiConsumerOnce<T, U>` instance with the specified name
357 ///
358 /// # Examples
359 ///
360 /// ```rust
361 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
362 /// use std::sync::{Arc, Mutex};
363 ///
364 /// let log = Arc::new(Mutex::new(Vec::new()));
365 /// let l = log.clone();
366 /// let consumer = BoxBiConsumerOnce::new_with_name("sum_logger", move |x: &i32, y: &i32| {
367 /// l.lock().unwrap().push(*x + *y);
368 /// });
369 /// assert_eq!(consumer.name(), Some("sum_logger"));
370 /// consumer.accept_once(&5, &3);
371 /// assert_eq!(*log.lock().unwrap(), vec![8]);
372 /// ```
373 pub fn new_with_name<F>(name: &str, f: F) -> Self
374 where
375 F: FnOnce(&T, &U) + 'static,
376 {
377 BoxBiConsumerOnce {
378 function: Box::new(f),
379 name: Some(name.to_string()),
380 }
381 }
382
383 /// Gets the name of the consumer
384 pub fn name(&self) -> Option<&str> {
385 self.name.as_deref()
386 }
387
388 /// Sets the name of the consumer
389 pub fn set_name(&mut self, name: impl Into<String>) {
390 self.name = Some(name.into());
391 }
392
393 /// Creates a no-op bi-consumer
394 ///
395 /// # Returns
396 ///
397 /// Returns a no-op bi-consumer
398 ///
399 /// # Examples
400 ///
401 /// ```rust
402 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
403 ///
404 /// let noop = BoxBiConsumerOnce::<i32, i32>::noop();
405 /// noop.accept_once(&42, &10);
406 /// // Values unchanged
407 /// ```
408 pub fn noop() -> Self {
409 BoxBiConsumerOnce::new(|_, _| {})
410 }
411
412 /// Chains another one-time bi-consumer in sequence
413 ///
414 /// Returns a new consumer executing the current operation first, then
415 /// the next operation. Consumes self.
416 ///
417 /// # Type Parameters
418 ///
419 /// * `C` - The type of the next consumer
420 ///
421 /// # Parameters
422 ///
423 /// * `next` - The consumer to execute after the current operation. **Note:
424 /// This parameter is passed by value and will transfer ownership.** Since
425 /// `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
426 /// Can be:
427 /// - A closure: `|x: &T, y: &U|`
428 /// - A `BoxBiConsumerOnce<T, U>`
429 /// - Any type implementing `BiConsumerOnce<T, U>`
430 ///
431 /// # Returns
432 ///
433 /// Returns a new composed `BoxBiConsumerOnce<T, U>`
434 ///
435 /// # Examples
436 ///
437 /// ```rust
438 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
439 /// use std::sync::{Arc, Mutex};
440 ///
441 /// let log = Arc::new(Mutex::new(Vec::new()));
442 /// let l1 = log.clone();
443 /// let l2 = log.clone();
444 /// let first = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
445 /// l1.lock().unwrap().push(*x + *y);
446 /// });
447 /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
448 /// l2.lock().unwrap().push(*x * *y);
449 /// });
450 ///
451 /// // Both first and second are moved and consumed
452 /// let chained = first.and_then(second);
453 /// chained.accept_once(&5, &3);
454 /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
455 /// // first.accept(&2, &3); // Would not compile - moved
456 /// // second.accept(&2, &3); // Would not compile - moved
457 /// ```
458 pub fn and_then<C>(self, next: C) -> Self
459 where
460 C: BiConsumerOnce<T, U> + 'static,
461 {
462 let first = self.function;
463 let second = next;
464 BoxBiConsumerOnce::new(move |t, u| {
465 first(t, u);
466 second.accept_once(t, u);
467 })
468 }
469
470 /// Creates a conditional bi-consumer
471 ///
472 /// Returns a bi-consumer that only executes when a predicate is satisfied.
473 ///
474 /// # Type Parameters
475 ///
476 /// * `P` - The predicate type
477 ///
478 /// # Parameters
479 ///
480 /// * `predicate` - The condition to check. **Note: This parameter is passed
481 /// by value and will transfer ownership.** If you need to preserve the
482 /// original bi-predicate, clone it first (if it implements `Clone`). Can be:
483 /// - A closure: `|x: &T, y: &U| -> bool`
484 /// - A function pointer: `fn(&T, &U) -> bool`
485 /// - A `BoxBiPredicate<T, U>`
486 /// - Any type implementing `BiPredicate<T, U>`
487 ///
488 /// # Returns
489 ///
490 /// Returns `BoxConditionalBiConsumerOnce<T, U>`
491 ///
492 /// # Examples
493 ///
494 /// ```rust
495 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
496 /// use std::sync::{Arc, Mutex};
497 ///
498 /// let log = Arc::new(Mutex::new(Vec::new()));
499 /// let l = log.clone();
500 /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
501 /// l.lock().unwrap().push(*x + *y);
502 /// });
503 /// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
504 ///
505 /// conditional.accept_once(&5, &3);
506 /// assert_eq!(*log.lock().unwrap(), vec![8]);
507 /// ```
508 pub fn when<P>(self, predicate: P) -> BoxConditionalBiConsumerOnce<T, U>
509 where
510 P: BiPredicate<T, U> + 'static,
511 {
512 BoxConditionalBiConsumerOnce {
513 consumer: self,
514 predicate: predicate.into_box(),
515 }
516 }
517}
518
519impl<T, U> BiConsumerOnce<T, U> for BoxBiConsumerOnce<T, U> {
520 fn accept_once(self, first: &T, second: &U) {
521 (self.function)(first, second)
522 }
523
524 fn into_box_once(self) -> BoxBiConsumerOnce<T, U>
525 where
526 T: 'static,
527 U: 'static,
528 {
529 self
530 }
531
532 fn into_fn_once(self) -> impl FnOnce(&T, &U)
533 where
534 T: 'static,
535 U: 'static,
536 {
537 self.function
538 }
539}
540
541impl<T, U> fmt::Debug for BoxBiConsumerOnce<T, U> {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 f.debug_struct("BoxBiConsumerOnce")
544 .field("name", &self.name)
545 .field("function", &"<function>")
546 .finish()
547 }
548}
549
550impl<T, U> fmt::Display for BoxBiConsumerOnce<T, U> {
551 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552 match &self.name {
553 Some(name) => write!(f, "BoxBiConsumerOnce({})", name),
554 None => write!(f, "BoxBiConsumerOnce"),
555 }
556 }
557}
558
559// =======================================================================
560// 3. BoxConditionalBiConsumerOnce - Box-based Conditional BiConsumerOnce
561// =======================================================================
562
563/// BoxConditionalBiConsumerOnce struct
564///
565/// A conditional one-time bi-consumer that only executes when a predicate is satisfied.
566/// Uses `BoxBiConsumerOnce` and `BoxBiPredicate` for single ownership semantics.
567///
568/// This type is typically created by calling `BoxBiConsumerOnce::when()` and is
569/// designed to work with the `or_else()` method to create if-then-else logic.
570///
571/// # Features
572///
573/// - **Single Ownership**: Not cloneable, consumes `self` on use
574/// - **Conditional Execution**: Only consumes when predicate returns `true`
575/// - **Chainable**: Can add `or_else` branch to create if-then-else logic
576/// - **Implements BiConsumerOnce**: Can be used anywhere a `BiConsumerOnce` is expected
577///
578/// # Examples
579///
580/// ## Basic Conditional Execution
581///
582/// ```rust
583/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
584/// use std::sync::{Arc, Mutex};
585///
586/// let log = Arc::new(Mutex::new(Vec::new()));
587/// let l = log.clone();
588/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
589/// l.lock().unwrap().push(*x + *y);
590/// });
591/// let conditional = consumer.when(|x: &i32, y: &i32| *x > 0 && *y > 0);
592///
593/// conditional.accept_once(&5, &3);
594/// assert_eq!(*log.lock().unwrap(), vec![8]); // Executed
595/// ```
596///
597/// ## With or_else Branch
598///
599/// ```rust
600/// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
601/// use std::sync::{Arc, Mutex};
602///
603/// let log = Arc::new(Mutex::new(Vec::new()));
604/// let l1 = log.clone();
605/// let l2 = log.clone();
606/// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
607/// l1.lock().unwrap().push(*x + *y);
608/// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
609/// .or_else(move |x: &i32, y: &i32| {
610/// l2.lock().unwrap().push(*x * *y);
611/// });
612///
613/// consumer.accept_once(&5, &3);
614/// assert_eq!(*log.lock().unwrap(), vec![8]); // when branch executed
615/// ```
616///
617/// # Author
618///
619/// Haixing Hu
620pub struct BoxConditionalBiConsumerOnce<T, U> {
621 consumer: BoxBiConsumerOnce<T, U>,
622 predicate: BoxBiPredicate<T, U>,
623}
624
625impl<T, U> BiConsumerOnce<T, U> for BoxConditionalBiConsumerOnce<T, U>
626where
627 T: 'static,
628 U: 'static,
629{
630 fn accept_once(self, first: &T, second: &U) {
631 if self.predicate.test(first, second) {
632 self.consumer.accept_once(first, second);
633 }
634 }
635
636 fn into_box_once(self) -> BoxBiConsumerOnce<T, U> {
637 let pred = self.predicate;
638 let consumer = self.consumer;
639 BoxBiConsumerOnce::new(move |t, u| {
640 if pred.test(t, u) {
641 consumer.accept_once(t, u);
642 }
643 })
644 }
645
646 fn into_fn_once(self) -> impl FnOnce(&T, &U) {
647 let pred = self.predicate;
648 let consumer = self.consumer;
649 move |t: &T, u: &U| {
650 if pred.test(t, u) {
651 consumer.accept_once(t, u);
652 }
653 }
654 }
655}
656
657impl<T, U> BoxConditionalBiConsumerOnce<T, U>
658where
659 T: 'static,
660 U: 'static,
661{
662 /// Chains another consumer in sequence
663 ///
664 /// Combines the current conditional consumer with another consumer into a new
665 /// consumer. The current conditional consumer executes first, followed by the
666 /// next consumer.
667 ///
668 /// # Parameters
669 ///
670 /// * `next` - The next consumer to execute. **Note: This parameter is passed
671 /// by value and will transfer ownership.** Since `BoxBiConsumerOnce`
672 /// cannot be cloned, the parameter will be consumed. Can be:
673 /// - A closure: `|x: &T, y: &U|`
674 /// - A `BoxBiConsumerOnce<T, U>`
675 /// - Any type implementing `BiConsumerOnce<T, U>`
676 ///
677 /// # Returns
678 ///
679 /// Returns a new `BoxBiConsumerOnce<T, U>`
680 ///
681 /// # Examples
682 ///
683 /// ```rust
684 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
685 /// use std::sync::{Arc, Mutex};
686 ///
687 /// let log = Arc::new(Mutex::new(Vec::new()));
688 /// let l1 = log.clone();
689 /// let l2 = log.clone();
690 /// let cond = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
691 /// l1.lock().unwrap().push(*x + *y);
692 /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0);
693 /// let second = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
694 /// l2.lock().unwrap().push(*x * *y);
695 /// });
696 ///
697 /// // Both cond and second are moved and consumed
698 /// let chained = cond.and_then(second);
699 /// chained.accept_once(&5, &3);
700 /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
701 /// // cond.accept(&2, &3); // Would not compile - moved
702 /// // second.accept(&2, &3); // Would not compile - moved
703 /// ```
704 pub fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
705 where
706 C: BiConsumerOnce<T, U> + 'static,
707 {
708 let first = self;
709 let second = next;
710 BoxBiConsumerOnce::new(move |t, u| {
711 first.accept_once(t, u);
712 second.accept_once(t, u);
713 })
714 }
715
716 /// Adds an else branch
717 ///
718 /// Executes the original consumer when the condition is satisfied, otherwise
719 /// executes else_consumer.
720 ///
721 /// # Parameters
722 ///
723 /// * `else_consumer` - The consumer for the else branch. **Note: This parameter
724 /// is passed by value and will transfer ownership.** Since
725 /// `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
726 /// Can be:
727 /// - A closure: `|x: &T, y: &U|`
728 /// - A `BoxBiConsumerOnce<T, U>`
729 /// - Any type implementing `BiConsumerOnce<T, U>`
730 ///
731 /// # Returns
732 ///
733 /// Returns the composed `BoxBiConsumerOnce<T, U>`
734 ///
735 /// # Examples
736 ///
737 /// ## Using a closure (recommended)
738 ///
739 /// ```rust
740 /// use prism3_function::{BiConsumerOnce, BoxBiConsumerOnce};
741 /// use std::sync::{Arc, Mutex};
742 ///
743 /// let log = Arc::new(Mutex::new(Vec::new()));
744 /// let l1 = log.clone();
745 /// let l2 = log.clone();
746 /// let consumer = BoxBiConsumerOnce::new(move |x: &i32, y: &i32| {
747 /// l1.lock().unwrap().push(*x + *y);
748 /// }).when(|x: &i32, y: &i32| *x > 0 && *y > 0)
749 /// .or_else(move |x: &i32, y: &i32| {
750 /// l2.lock().unwrap().push(*x * *y);
751 /// });
752 ///
753 /// consumer.accept_once(&5, &3);
754 /// assert_eq!(*log.lock().unwrap(), vec![8]); // Condition satisfied
755 /// ```
756 pub fn or_else<C>(self, else_consumer: C) -> BoxBiConsumerOnce<T, U>
757 where
758 C: BiConsumerOnce<T, U> + 'static,
759 {
760 let pred = self.predicate;
761 let then_cons = self.consumer;
762 let else_cons = else_consumer;
763 BoxBiConsumerOnce::new(move |t, u| {
764 if pred.test(t, u) {
765 then_cons.accept_once(t, u);
766 } else {
767 else_cons.accept_once(t, u);
768 }
769 })
770 }
771}
772
773// =======================================================================
774// 5. Implement BiConsumerOnce trait for closures
775// =======================================================================
776
777/// Implements BiConsumerOnce for all FnOnce(&T, &U)
778impl<T, U, F> BiConsumerOnce<T, U> for F
779where
780 F: FnOnce(&T, &U),
781{
782 fn accept_once(self, first: &T, second: &U) {
783 self(first, second)
784 }
785
786 fn into_box_once(self) -> BoxBiConsumerOnce<T, U>
787 where
788 Self: Sized + 'static,
789 T: 'static,
790 U: 'static,
791 {
792 BoxBiConsumerOnce::new(self)
793 }
794
795 fn into_fn_once(self) -> impl FnOnce(&T, &U)
796 where
797 Self: Sized + 'static,
798 T: 'static,
799 U: 'static,
800 {
801 self
802 }
803
804 fn to_box_once(&self) -> BoxBiConsumerOnce<T, U>
805 where
806 Self: Sized + Clone + 'static,
807 T: 'static,
808 U: 'static,
809 {
810 BoxBiConsumerOnce::new(self.clone())
811 }
812
813 fn to_fn_once(&self) -> impl FnOnce(&T, &U)
814 where
815 Self: Sized + Clone + 'static,
816 T: 'static,
817 U: 'static,
818 {
819 self.clone()
820 }
821}
822
823// =======================================================================
824// 6. Provide extension methods for closures
825// =======================================================================
826
827/// Extension trait providing one-time bi-consumer composition methods for
828/// closures
829///
830/// Provides `and_then` and other composition methods for all closures
831/// implementing `FnOnce(&T, &U)`, enabling direct method chaining on
832/// closures without explicit wrapper types.
833///
834/// # Features
835///
836/// - **Natural Syntax**: Chain operations directly on closures
837/// - **Returns BoxBiConsumerOnce**: Composition results can be further
838/// chained
839/// - **Zero Cost**: No overhead when composing closures
840/// - **Automatic Implementation**: All `FnOnce(&T, &U)` closures get
841/// these methods automatically
842///
843/// # Examples
844///
845/// ```rust
846/// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
847/// use std::sync::{Arc, Mutex};
848///
849/// let log = Arc::new(Mutex::new(Vec::new()));
850/// let l1 = log.clone();
851/// let l2 = log.clone();
852/// let chained = (move |x: &i32, y: &i32| {
853/// l1.lock().unwrap().push(*x + *y);
854/// }).and_then(move |x: &i32, y: &i32| {
855/// l2.lock().unwrap().push(*x * *y);
856/// });
857/// chained.accept_once(&5, &3);
858/// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
859/// ```
860///
861/// # Author
862///
863/// Haixing Hu
864pub trait FnBiConsumerOnceOps<T, U>: FnOnce(&T, &U) + Sized {
865 /// Chains another one-time bi-consumer in sequence
866 ///
867 /// Returns a new consumer executing the current operation first, then
868 /// the next operation. Consumes the current closure and returns
869 /// `BoxBiConsumerOnce<T, U>`.
870 ///
871 /// # Type Parameters
872 ///
873 /// * `C` - The type of the next consumer
874 ///
875 /// # Parameters
876 ///
877 /// * `next` - The consumer to execute after the current operation. **Note:
878 /// This parameter is passed by value and will transfer ownership.** Since
879 /// `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
880 /// Can be:
881 /// - A closure: `|x: &T, y: &U|`
882 /// - A `BoxBiConsumerOnce<T, U>`
883 /// - Any type implementing `BiConsumerOnce<T, U>`
884 ///
885 /// # Returns
886 ///
887 /// Returns the composed `BoxBiConsumerOnce<T, U>`
888 ///
889 /// # Examples
890 ///
891 /// ```rust
892 /// use prism3_function::{BiConsumerOnce, FnBiConsumerOnceOps};
893 /// use std::sync::{Arc, Mutex};
894 ///
895 /// let log = Arc::new(Mutex::new(Vec::new()));
896 /// let l1 = log.clone();
897 /// let l2 = log.clone();
898 /// let chained = (move |x: &i32, y: &i32| {
899 /// l1.lock().unwrap().push(*x + *y);
900 /// }).and_then(move |x: &i32, y: &i32| {
901 /// l2.lock().unwrap().push(*x * *y);
902 /// }).and_then(|x: &i32, y: &i32| {
903 /// println!("Result: {}, {}", x, y);
904 /// });
905 ///
906 /// chained.accept_once(&5, &3);
907 /// assert_eq!(*log.lock().unwrap(), vec![8, 15]);
908 /// ```
909 fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
910 where
911 Self: 'static,
912 C: BiConsumerOnce<T, U> + 'static,
913 T: 'static,
914 U: 'static,
915 {
916 let first = self;
917 let second = next;
918 BoxBiConsumerOnce::new(move |t, u| {
919 first(t, u);
920 second.accept_once(t, u);
921 })
922 }
923}
924
925/// Implements FnBiConsumerOnceOps for all closure types
926impl<T, U, F> FnBiConsumerOnceOps<T, U> for F where F: FnOnce(&T, &U) {}