use std::sync::Arc;
use crate::{
AsyncNext, AsyncPipe, AsyncPipelineFuture, AsyncPipelineStep, FinallyCallback, PipelineError,
PipelineResult,
};
pub struct AsyncPipeline<TPassable, TError = PipelineError> {
passable: Option<TPassable>,
pipes: Vec<AsyncPipelineStep<TPassable, TError>>,
finally: Option<FinallyCallback<TPassable>>,
}
impl<TPassable, TError> AsyncPipeline<TPassable, TError> {
pub fn new() -> Self {
Self {
passable: None,
pipes: Vec::new(),
finally: None,
}
}
pub fn send(mut self, passable: TPassable) -> Self {
self.passable = Some(passable);
self
}
pub fn pipe<TPipe>(mut self, pipe: TPipe) -> Self
where
TPassable: Send,
TError: Send,
TPipe: AsyncPipe<TPassable, TError> + Send + Sync + 'static,
{
self.pipes.push(Arc::new(pipe));
self
}
pub fn through(mut self, pipes: Vec<AsyncPipelineStep<TPassable, TError>>) -> Self {
self.pipes = pipes;
self
}
pub fn when<TPipe>(mut self, condition: bool, pipe: TPipe) -> Self
where
TPassable: Send,
TError: Send,
TPipe: AsyncPipe<TPassable, TError> + Send + Sync + 'static,
{
if condition {
self.pipes.push(Arc::new(pipe));
}
self
}
pub fn unless<TPipe>(mut self, condition: bool, pipe: TPipe) -> Self
where
TPassable: Send,
TError: Send,
TPipe: AsyncPipe<TPassable, TError> + Send + Sync + 'static,
{
if !condition {
self.pipes.push(Arc::new(pipe));
}
self
}
pub fn finally<F>(mut self, callback: F) -> Self
where
F: Fn(&PipelineResult<TPassable>) + Send + Sync + 'static,
{
self.finally = Some(Box::new(callback));
self
}
}
impl<TPassable, TError> AsyncPipeline<TPassable, TError>
where
TPassable: Send + 'static,
TError: Into<PipelineError> + Send + 'static,
{
pub async fn then<TDestination, TResult>(
self,
destination: TDestination,
) -> PipelineResult<TResult>
where
TDestination: FnOnce(TPassable) -> TResult,
{
self.run().await.map(destination)
}
pub async fn then_return(self) -> PipelineResult<TPassable> {
self.run().await
}
pub async fn rescue<TRecovery>(self, recovery: TRecovery) -> PipelineResult<TPassable>
where
TRecovery: FnOnce(PipelineError) -> TPassable,
{
match self.then_return().await {
| Ok(passable) => Ok(passable),
| Err(PipelineError::InputMissing) => Err(PipelineError::InputMissing),
| Err(err) => Ok(recovery(err)),
}
}
async fn run(self) -> PipelineResult<TPassable> {
let passable = self.passable.ok_or(PipelineError::InputMissing)?;
let destination = |passable| {
Box::pin(async move { Ok(passable) }) as AsyncPipelineFuture<'_, TPassable, TError>
};
let next = AsyncNext::new(&self.pipes, &destination);
let result = next.handle(passable).await.map_err(Into::into);
if let Some(finally) = &self.finally {
finally(&result);
}
result
}
}
impl<TPassable, TError> Default for AsyncPipeline<TPassable, TError> {
fn default() -> Self {
Self::new()
}
}