use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::{Input, InputHandler, NoemaResult};
pub struct Next<I: Input + Send + Sync + 'static> {
run: Box<
dyn FnOnce(Arc<I>) -> Pin<Box<dyn Future<Output = NoemaResult<I::Output>> + Send>> + Send,
>,
}
impl<I: Input + Send + Sync + 'static> Next<I> {
pub(crate) fn new<F>(f: F) -> Self
where
F: FnOnce(Arc<I>) -> Pin<Box<dyn Future<Output = NoemaResult<I::Output>> + Send>>
+ Send
+ 'static,
{
Self { run: Box::new(f) }
}
pub async fn run(self, input: Arc<I>) -> NoemaResult<I::Output> {
(self.run)(input).await
}
}
#[async_trait::async_trait]
pub trait PipelineBehavior<I: Input + Send + Sync + 'static>: Send + Sync {
async fn handle(&self, input: Arc<I>, next: Next<I>) -> NoemaResult<I::Output>;
}
fn run_at_index<I: Input + Send + Sync + 'static>(
index: usize,
behaviors: Arc<[Arc<dyn PipelineBehavior<I> + Send + Sync>]>,
handler: Arc<dyn InputHandler<I> + Send + Sync>,
input: Arc<I>,
) -> Pin<Box<dyn Future<Output = NoemaResult<I::Output>> + Send>> {
Box::pin(async move {
if index >= behaviors.len() {
return handler.handle(input).await;
}
let behavior = behaviors[index].clone();
let behaviors_next = behaviors.clone();
let handler_next = handler.clone();
let next =
Next::new(move |input| run_at_index(index + 1, behaviors_next, handler_next, input));
behavior.handle(input, next).await
})
}
pub async fn run_pipeline<I: Input + Send + Sync + 'static>(
input: Arc<I>,
handler: Arc<dyn InputHandler<I> + Send + Sync>,
behaviors: Arc<[Arc<dyn PipelineBehavior<I> + Send + Sync>]>,
) -> NoemaResult<I::Output> {
run_at_index(0, behaviors, handler, input).await
}