command_stream/
stream_processing.rs1use crate::{OutputChunk, OutputStream};
3use std::collections::VecDeque;
4use std::future::Future;
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7use tokio::sync::mpsc;
8use tokio::task::JoinHandle;
9
10struct Task(JoinHandle<()>);
11impl Drop for Task {
12 fn drop(&mut self) {
13 self.0.abort();
14 }
15}
16
17pub struct DataStream<T> {
19 receiver: mpsc::Receiver<T>,
20 task: Arc<Task>,
21}
22
23#[derive(Debug, Clone, Default)]
24pub struct StreamStats {
25 pub count: u64,
26 pub errors: u64,
27 pub error_rate: f64,
28 pub throughput: f64,
29 pub elapsed: f64,
30 pub response_count: u64,
31 pub response_mean: f64,
32 pub response_min: Option<f64>,
33 pub response_max: Option<f64>,
34}
35
36impl<T: Send + 'static> DataStream<T> {
37 fn spawn<F, Fut>(producer: F) -> Self
38 where
39 F: FnOnce(mpsc::Sender<T>) -> Fut,
40 Fut: Future<Output = ()> + Send + 'static,
41 {
42 let (tx, receiver) = mpsc::channel(32);
43 let task = Arc::new(Task(tokio::spawn(producer(tx))));
44 Self { receiver, task }
45 }
46 pub fn from_values<I>(values: I) -> Self
47 where
48 I: IntoIterator<Item = T> + Send + 'static,
49 I::IntoIter: Send,
50 {
51 Self::spawn(|tx| async move {
52 for value in values {
53 if tx.send(value).await.is_err() {
54 break;
55 }
56 }
57 })
58 }
59 pub async fn next(&mut self) -> Option<T> {
60 self.receiver.recv().await
61 }
62 pub fn map<U, F>(self, mut transform: F) -> DataStream<U>
63 where
64 U: Send + 'static,
65 F: FnMut(T) -> U + Send + 'static,
66 {
67 self.map_async(move |value| std::future::ready(transform(value)))
68 }
69 pub fn map_async<U, F, Fut>(mut self, mut transform: F) -> DataStream<U>
70 where
71 U: Send + 'static,
72 F: FnMut(T) -> Fut + Send + 'static,
73 Fut: Future<Output = U> + Send,
74 {
75 DataStream::spawn(|tx| async move {
76 while let Some(value) = self.next().await {
77 if tx.send(transform(value).await).await.is_err() {
78 break;
79 }
80 }
81 })
82 }
83 pub fn filter<F>(mut self, mut predicate: F) -> Self
84 where
85 F: FnMut(&T) -> bool + Send + 'static,
86 {
87 Self::spawn(|tx| async move {
88 while let Some(value) = self.next().await {
89 if predicate(&value) && tx.send(value).await.is_err() {
90 break;
91 }
92 }
93 })
94 }
95 pub fn batch(self, size: usize) -> DataStream<Vec<T>> {
96 self.batch_inner(size, None)
97 }
98 pub fn batch_with_timeout(self, size: usize, time_window: Duration) -> DataStream<Vec<T>> {
101 assert!(!time_window.is_zero(), "time window must be positive");
102 assert!(
103 tokio::time::Instant::now()
104 .checked_add(time_window)
105 .is_some(),
106 "time window must fit the clock"
107 );
108 self.batch_inner(size, Some(time_window))
109 }
110 fn batch_inner(mut self, size: usize, time_window: Option<Duration>) -> DataStream<Vec<T>> {
111 assert!(size > 0);
112 DataStream::spawn(|tx| async move {
113 let mut values = Vec::with_capacity(size);
114 let mut deadline = None;
115 loop {
116 let value = if let Some(end) = deadline {
117 let read = if tokio::time::Instant::now() >= end {
120 None
121 } else {
122 tokio::time::timeout_at(end, self.next()).await.ok()
123 };
124 match read {
125 Some(value) => value,
126 None => {
127 if tx.send(std::mem::take(&mut values)).await.is_err() {
128 return;
129 }
130 deadline = None;
131 continue;
132 }
133 }
134 } else {
135 self.next().await
136 };
137 let Some(value) = value else { break };
138 if values.is_empty() {
139 deadline = time_window.map(|duration| tokio::time::Instant::now() + duration);
140 }
141 values.push(value);
142 if values.len() == size {
143 if tx.send(std::mem::take(&mut values)).await.is_err() {
144 return;
145 }
146 deadline = None;
147 }
148 }
149 if !values.is_empty() {
150 let _ = tx.send(values).await;
151 }
152 })
153 }
154 pub fn window(mut self, size: usize) -> DataStream<Vec<T>>
155 where
156 T: Clone,
157 {
158 assert!(size > 0);
159 DataStream::spawn(|tx| async move {
160 let mut values = VecDeque::with_capacity(size);
161 while let Some(value) = self.next().await {
162 values.push_back(value);
163 if values.len() > size {
164 values.pop_front();
165 }
166 if values.len() == size && tx.send(values.iter().cloned().collect()).await.is_err()
167 {
168 break;
169 }
170 }
171 })
172 }
173 pub fn sliding_window(self, size: usize) -> DataStream<Vec<T>>
175 where
176 T: Clone,
177 {
178 self.window(size)
179 }
180 pub fn split<F>(mut self, mut predicate: F) -> (Self, Self)
181 where
182 F: FnMut(&T) -> bool + Send + 'static,
183 {
184 let (yes_tx, yes) = mpsc::channel(32);
185 let (no_tx, no) = mpsc::channel(32);
186 let task = Arc::new(Task(tokio::spawn(async move {
187 while let Some(value) = self.next().await {
188 if yes_tx.is_closed() && no_tx.is_closed() {
189 break;
190 }
191 let tx = if predicate(&value) { &yes_tx } else { &no_tx };
192 let _ = tx.send(value).await;
193 }
194 })));
195 (
196 Self {
197 receiver: yes,
198 task: task.clone(),
199 },
200 Self { receiver: no, task },
201 )
202 }
203 pub fn merge(mut self, mut other: Self) -> Self {
204 Self::spawn(|tx| async move {
205 let mut left = true;
206 let mut right = true;
207 while left || right {
208 let value = tokio::select! { value = self.next(), if left => { if value.is_none() { left = false; } value },
209 value = other.next(), if right => { if value.is_none() { right = false; } value } };
210 if let Some(value) = value {
211 if tx.send(value).await.is_err() {
212 break;
213 }
214 }
215 }
216 })
217 }
218 pub async fn collect(mut self) -> Vec<T> {
219 let mut values = Vec::new();
220 while let Some(value) = self.next().await {
221 values.push(value);
222 }
223 values
224 }
225 pub async fn reduce<U, F>(mut self, mut initial: U, mut reducer: F) -> U
226 where
227 F: FnMut(U, T) -> U,
228 {
229 while let Some(value) = self.next().await {
230 initial = reducer(initial, value);
231 }
232 initial
233 }
234 pub async fn for_each<F, Fut>(mut self, mut callback: F)
235 where
236 F: FnMut(T) -> Fut,
237 Fut: Future<Output = ()>,
238 {
239 while let Some(value) = self.next().await {
240 callback(value).await;
241 }
242 }
243 pub async fn analyze<E, R, U>(
244 mut self,
245 mut error: E,
246 mut response: R,
247 mut update: U,
248 ) -> StreamStats
249 where
250 E: FnMut(&T) -> bool,
251 R: FnMut(&T) -> Option<f64>,
252 U: FnMut(&StreamStats),
253 {
254 let started = Instant::now();
255 let mut stats = StreamStats::default();
256 let mut sum = 0.0;
257 while let Some(value) = self.next().await {
258 stats.count += 1;
259 stats.errors += u64::from(error(&value));
260 if let Some(time) = response(&value).filter(|time| time.is_finite()) {
261 stats.response_count += 1;
262 sum += time;
263 stats.response_min = Some(stats.response_min.map_or(time, |min| min.min(time)));
264 stats.response_max = Some(stats.response_max.map_or(time, |max| max.max(time)));
265 stats.response_mean = sum / stats.response_count as f64;
266 }
267 stats.elapsed = started.elapsed().as_secs_f64();
268 stats.error_rate = stats.errors as f64 / stats.count as f64;
269 stats.throughput = stats.count as f64 / stats.elapsed.max(0.001);
270 update(&stats);
271 }
272 stats
273 }
274}
275
276impl<T> Drop for DataStream<T> {
277 fn drop(&mut self) {
278 self.receiver.close();
279 let _ = &self.task;
280 }
281}
282
283impl OutputStream {
284 pub fn lines(mut self) -> DataStream<String> {
286 DataStream::spawn(|tx| async move {
287 let mut pending = Vec::new();
288 while let Some(chunk) = self.next().await {
289 if let OutputChunk::Stdout(bytes) = chunk {
290 pending.extend(bytes);
291 while let Some(end) = pending.iter().position(|byte| *byte == b'\n') {
292 let mut line = pending.drain(..=end).collect::<Vec<_>>();
293 line.pop();
294 if line.last() == Some(&b'\r') {
295 line.pop();
296 }
297 if tx
298 .send(String::from_utf8_lossy(&line).into_owned())
299 .await
300 .is_err()
301 {
302 return;
303 }
304 }
305 }
306 }
307 if !pending.is_empty() {
308 let _ = tx
309 .send(String::from_utf8_lossy(&pending).into_owned())
310 .await;
311 }
312 })
313 }
314}