futures_concurrency_dynamic/handle.rs
1//! Handle-based API for DynamicMerge that allows separate mutable access.
2//!
3//! This module provides a way to split the dynamic merge into two components:
4//! - A `DynamicMergeStream` that implements `Stream` and can be polled
5//! - A `DynamicMergeHandle` that can push new streams
6//!
7//! This allows different parts of your code to hold mutable references to each independently.
8
9use crate::DynamicMerge;
10use futures_core::Stream;
11use futures_util::stream::SelectAll;
12use std::pin::Pin;
13use std::sync::{Arc, Mutex};
14use std::task::{Context, Poll};
15
16/// A handle for pushing new streams into a `DynamicMergeStream`.
17///
18/// This handle shares ownership of the underlying `DynamicMerge` with the
19/// `DynamicMergeStream`, allowing you to push new streams while the stream is
20/// being polled elsewhere.
21///
22/// # Examples
23///
24/// ```
25/// use futures_concurrency_dynamic::dynamic_merge_with_handle;
26/// use futures_util::stream::{self, StreamExt};
27///
28/// # async fn example() {
29/// let (mut stream, mut handle) = dynamic_merge_with_handle::<i32>();
30///
31/// // Push streams via the handle
32/// handle.push(stream::iter(vec![1, 2, 3]));
33/// handle.push(stream::iter(vec![4, 5, 6]));
34///
35/// // Poll the stream elsewhere
36/// let items: Vec<i32> = stream.collect().await;
37/// # }
38/// ```
39pub struct DynamicMergeHandle<'a, T> {
40 shared: Arc<Mutex<DynamicMerge<'a, T>>>,
41}
42
43impl<'a, T> DynamicMergeHandle<'a, T> {
44 /// Pushes a new stream into the merge.
45 ///
46 /// The stream will be polled concurrently with other streams. When the stream
47 /// produces an item, it will be returned from the merge. When the stream
48 /// completes, it is automatically removed.
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use futures_concurrency_dynamic::dynamic_merge_with_handle;
54 /// use futures_util::stream;
55 ///
56 /// let (stream, mut handle) = dynamic_merge_with_handle::<i32>();
57 /// handle.push(stream::iter(vec![1, 2, 3]));
58 /// handle.push(stream::iter(vec![4, 5, 6]));
59 /// ```
60 pub fn push<S>(&mut self, stream: S)
61 where
62 S: Stream<Item = T> + Send + 'a,
63 {
64 self.shared.lock().unwrap().push(Box::pin(stream));
65 }
66
67 /// Returns the number of active streams currently in the merge.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// use futures_concurrency_dynamic::dynamic_merge_with_handle;
73 /// use futures_util::stream;
74 ///
75 /// let (stream, mut handle) = dynamic_merge_with_handle::<i32>();
76 /// assert_eq!(handle.len(), 0);
77 ///
78 /// handle.push(stream::iter(vec![1, 2, 3]));
79 /// assert_eq!(handle.len(), 1);
80 /// ```
81 pub fn len(&self) -> usize {
82 self.shared.lock().unwrap().len()
83 }
84
85 /// Returns `true` if there are no active streams in the merge.
86 ///
87 /// # Examples
88 ///
89 /// ```
90 /// use futures_concurrency_dynamic::dynamic_merge_with_handle;
91 ///
92 /// let (stream, handle) = dynamic_merge_with_handle::<i32>();
93 /// assert!(handle.is_empty());
94 /// ```
95 pub fn is_empty(&self) -> bool {
96 self.shared.lock().unwrap().is_empty()
97 }
98
99 /// Clears all streams from the merge, removing all active streams.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// use futures_concurrency_dynamic::dynamic_merge_with_handle;
105 /// use futures_util::stream;
106 ///
107 /// let (stream, mut handle) = dynamic_merge_with_handle::<i32>();
108 /// handle.push(stream::iter(vec![1, 2, 3]));
109 /// assert_eq!(handle.len(), 1);
110 ///
111 /// handle.clear();
112 /// assert_eq!(handle.len(), 0);
113 /// ```
114 pub fn clear(&mut self) {
115 self.shared.lock().unwrap().clear();
116 }
117}
118
119impl<'a, T> Clone for DynamicMergeHandle<'a, T> {
120 fn clone(&self) -> Self {
121 Self {
122 shared: Arc::clone(&self.shared),
123 }
124 }
125}
126
127/// The stream component of a split dynamic merge.
128///
129/// This stream can be polled to receive items from all streams added via the
130/// corresponding `DynamicMergeHandle`.
131///
132/// Created via [`dynamic_merge_with_handle`].
133pub struct DynamicMergeStream<'a, T> {
134 shared: Arc<Mutex<DynamicMerge<'a, T>>>,
135}
136
137impl<'a, T> Stream for DynamicMergeStream<'a, T> {
138 type Item = T;
139
140 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
141 let mut shared = self.shared.lock().unwrap();
142 Pin::new(&mut *shared).poll_next(cx)
143 }
144}
145
146/// Creates a new dynamic merge with a separate handle for pushing streams.
147///
148/// Returns a tuple of `(stream, handle)` where:
149/// - `stream` implements `Stream` and yields items from all added streams
150/// - `handle` allows pushing new streams and controlling the merge lifecycle
151///
152/// This allows the stream and handle to be held by different parts of your code
153/// with independent mutable access.
154///
155/// # Examples
156///
157/// ```
158/// use futures_concurrency_dynamic::dynamic_merge_with_handle;
159/// use futures_util::stream::{self, StreamExt};
160///
161/// # async fn example() {
162/// let (mut stream, mut handle) = dynamic_merge_with_handle::<i32>();
163///
164/// // In one part of code: push streams
165/// handle.push(stream::iter(vec![1, 2, 3]));
166/// handle.push(stream::iter(vec![4, 5, 6]));
167///
168/// // In another part: consume the stream
169/// while let Some(item) = stream.next().await {
170/// println!("Got: {}", item);
171/// }
172/// # }
173/// ```
174///
175/// # Type Parameters
176///
177/// - `T`: The item type that all streams must produce
178///
179/// Note: All streams must be `Send` but no longer require `'static` lifetime.
180pub fn dynamic_merge_with_handle<'a, T>() -> (DynamicMergeStream<'a, T>, DynamicMergeHandle<'a, T>)
181{
182 let shared = Arc::new(Mutex::new(SelectAll::new()));
183 let stream = DynamicMergeStream {
184 shared: Arc::clone(&shared),
185 };
186 let handle = DynamicMergeHandle { shared };
187 (stream, handle)
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193 use futures_util::stream::{self, StreamExt};
194
195 #[tokio::test]
196 async fn test_basic_handle_usage() {
197 let (mut stream, mut handle) = dynamic_merge_with_handle::<i32>();
198
199 handle.push(stream::iter(vec![1, 2, 3]));
200 handle.push(stream::iter(vec![4, 5, 6]));
201
202 let mut items = Vec::new();
203 while let Some(item) = stream.next().await {
204 items.push(item);
205 }
206
207 items.sort();
208 assert_eq!(items, vec![1, 2, 3, 4, 5, 6]);
209 }
210
211 #[tokio::test]
212 async fn test_separate_mutable_access() {
213 let (mut stream, mut handle) = dynamic_merge_with_handle::<i32>();
214
215 // Push both streams first
216 handle.push(stream::iter(vec![1, 2, 3]));
217 handle.push(stream::iter(vec![4, 5, 6]));
218
219 // Then consume from the stream
220 let mut items = Vec::new();
221 while let Some(item) = stream.next().await {
222 items.push(item);
223 }
224
225 items.sort();
226 assert_eq!(items, vec![1, 2, 3, 4, 5, 6]);
227 }
228
229 #[tokio::test]
230 async fn test_handle_clone() {
231 let (mut stream, mut handle1) = dynamic_merge_with_handle::<i32>();
232 let mut handle2 = handle1.clone();
233
234 handle1.push(stream::iter(vec![1, 2, 3]));
235 handle2.push(stream::iter(vec![4, 5, 6]));
236
237 let mut items = Vec::new();
238 while let Some(item) = stream.next().await {
239 items.push(item);
240 }
241
242 items.sort();
243 assert_eq!(items, vec![1, 2, 3, 4, 5, 6]);
244 }
245
246 #[test]
247 fn test_handle_len_and_empty() {
248 let (_stream, mut handle) = dynamic_merge_with_handle::<i32>();
249 assert!(handle.is_empty());
250 assert_eq!(handle.len(), 0);
251
252 handle.push(stream::iter(vec![1, 2, 3]));
253 assert!(!handle.is_empty());
254 assert_eq!(handle.len(), 1);
255
256 handle.push(stream::iter(vec![4, 5, 6]));
257 assert_eq!(handle.len(), 2);
258 }
259
260 #[test]
261 fn test_handle_clear() {
262 let (_stream, mut handle) = dynamic_merge_with_handle::<i32>();
263
264 handle.push(stream::iter(vec![1, 2, 3]));
265 handle.push(stream::iter(vec![4, 5, 6]));
266 assert_eq!(handle.len(), 2);
267
268 handle.clear();
269 assert_eq!(handle.len(), 0);
270 assert!(handle.is_empty());
271 }
272
273 #[tokio::test]
274 async fn test_dynamic_push_while_polling() {
275 let (mut stream, mut handle) = dynamic_merge_with_handle::<i32>();
276
277 handle.push(stream::iter(vec![1, 2]));
278
279 let item1 = stream.next().await;
280 assert!(item1.is_some());
281
282 // Push more while already polling
283 handle.push(stream::iter(vec![3, 4]));
284
285 let item2 = stream.next().await;
286 assert!(item2.is_some());
287
288 let item3 = stream.next().await;
289 assert!(item3.is_some());
290
291 let item4 = stream.next().await;
292 assert!(item4.is_some());
293 }
294}