Skip to main content

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