Skip to main content

qubit_function/consumers/bi_consumer_once/
fn_bi_consumer_once_ops.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// qubit-style: allow explicit-imports
11//! Defines the `FnBiConsumerOnceOps` public type.
12
13use super::{
14    BiConsumerOnce,
15    BoxBiConsumerOnce,
16};
17
18// =======================================================================
19// 4. Provide extension methods for closures
20// =======================================================================
21
22/// Extension trait providing one-time bi-consumer composition methods for
23/// closures
24///
25/// Provides `and_then` and other composition methods for all closures
26/// implementing `FnOnce(&T, &U)`, enabling direct method chaining on
27/// closures without explicit wrapper types.
28///
29/// # Features
30///
31/// - **Natural Syntax**: Chain operations directly on closures
32/// - **Returns BoxBiConsumerOnce**: Composition results can be further
33///   chained
34/// - **Zero Cost**: No overhead when composing closures
35/// - **Automatic Implementation**: All `FnOnce(&T, &U)` closures get
36///   these methods automatically
37///
38/// # Examples
39///
40/// ```rust
41/// use qubit_function::{BiConsumerOnce, FnBiConsumerOnceOps};
42/// use std::sync::{Arc, Mutex};
43///
44/// let log = Arc::new(Mutex::new(Vec::new()));
45/// let l1 = log.clone();
46/// let l2 = log.clone();
47/// let chained = (move |x: &i32, y: &i32| {
48///     l1.lock().expect("mutex should not be poisoned").push(*x + *y);
49/// }).and_then(move |x: &i32, y: &i32| {
50///     l2.lock().expect("mutex should not be poisoned").push(*x * *y);
51/// });
52/// chained.accept(&5, &3);
53/// assert_eq!(*log.lock().expect("mutex should not be poisoned"), vec![8, 15]);
54/// ```
55///
56pub trait FnBiConsumerOnceOps<T, U>: FnOnce(&T, &U) + Sized {
57    /// Chains another one-time bi-consumer in sequence
58    ///
59    /// Returns a new consumer executing the current operation first, then
60    /// the next operation. Consumes the current closure and returns
61    /// `BoxBiConsumerOnce<T, U>`.
62    ///
63    /// # Type Parameters
64    ///
65    /// * `C` - The type of the next consumer
66    ///
67    /// # Parameters
68    ///
69    /// * `next` - The consumer to execute after the current operation. **Note:
70    ///   This parameter is passed by value and will transfer ownership.** Since
71    ///   `BoxBiConsumerOnce` cannot be cloned, the parameter will be consumed.
72    ///   Can be:
73    ///   - A closure: `|x: &T, y: &U|`
74    ///   - A `BoxBiConsumerOnce<T, U>`
75    ///   - Any type implementing `BiConsumerOnce<T, U>`
76    ///
77    /// # Returns
78    ///
79    /// Returns the composed `BoxBiConsumerOnce<T, U>`
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// use qubit_function::{BiConsumerOnce, FnBiConsumerOnceOps};
85    /// use std::sync::{Arc, Mutex};
86    ///
87    /// let log = Arc::new(Mutex::new(Vec::new()));
88    /// let l1 = log.clone();
89    /// let l2 = log.clone();
90    /// let chained = (move |x: &i32, y: &i32| {
91    ///     l1.lock().expect("mutex should not be poisoned").push(*x + *y);
92    /// }).and_then(move |x: &i32, y: &i32| {
93    ///     l2.lock().expect("mutex should not be poisoned").push(*x * *y);
94    /// }).and_then(|x: &i32, y: &i32| {
95    ///     println!("Result: {}, {}", x, y);
96    /// });
97    ///
98    /// chained.accept(&5, &3);
99    /// assert_eq!(*log.lock().expect("mutex should not be poisoned"), vec![8, 15]);
100    /// ```
101    fn and_then<C>(self, next: C) -> BoxBiConsumerOnce<T, U>
102    where
103        Self: 'static,
104        C: BiConsumerOnce<T, U> + 'static,
105        T: 'static,
106        U: 'static,
107    {
108        let first = self;
109        let second = next;
110        BoxBiConsumerOnce::new(move |t, u| {
111            first(t, u);
112            second.accept(t, u);
113        })
114    }
115}
116
117/// Implements FnBiConsumerOnceOps for all closure types
118impl<T, U, F> FnBiConsumerOnceOps<T, U> for F where F: FnOnce(&T, &U) {}