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) {}