use futures::Stream;
use rskit_errors::{AppError, AppResult};
use std::future::Future;
pub fn parallel<S, T, O, F, Fut>(
stream: S,
concurrency: usize,
f: F,
) -> impl Stream<Item = AppResult<O>> + Send + 'static
where
S: Stream<Item = T> + Send + 'static,
T: Send + 'static,
O: Send + 'static,
F: Fn(T) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = AppResult<O>> + Send + 'static,
{
use futures::StreamExt;
if concurrency == 0 {
futures::stream::once(async {
Err(AppError::invalid_input(
"concurrency",
"must be greater than zero",
))
})
.left_stream()
} else {
stream.map(f).buffer_unordered(concurrency).right_stream()
}
}
pub fn fan_out<S, T, O, F, Fut>(
stream: S,
max_branches: usize,
fns: Vec<F>,
) -> impl Stream<Item = AppResult<Vec<O>>> + Send + 'static
where
S: Stream<Item = T> + Send + 'static,
T: Clone + Send + 'static,
O: Send + 'static,
F: Fn(T) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = AppResult<O>> + Send + 'static,
{
use futures::StreamExt;
if max_branches == 0 || fns.len() > max_branches {
let error = AppError::invalid_input(
"max_branches",
format!("branch count {} exceeds limit {max_branches}", fns.len()),
);
futures::stream::once(async { Err(error) }).left_stream()
} else {
let fns = std::sync::Arc::new(fns);
stream
.then(move |item| {
let fns = fns.clone();
async move { futures::future::try_join_all(fns.iter().map(|f| f(item.clone()))).await }
})
.right_stream()
}
}