use crate::{FinallyCallback, Next, Pipe, PipelineError, PipelineResult, PipelineStep};
use std::sync::Arc;
pub struct Pipeline<TPassable, TError = PipelineError> {
passable: Option<TPassable>,
pipes: Vec<PipelineStep<TPassable, TError>>,
finally: Option<FinallyCallback<TPassable>>,
}
impl<TPassable, TError> Pipeline<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
TPipe: Pipe<TPassable, TError> + Send + Sync + 'static,
{
self.pipes.push(Arc::new(pipe));
self
}
pub fn through(mut self, pipes: Vec<PipelineStep<TPassable, TError>>) -> Self {
self.pipes = pipes;
self
}
pub fn when<P>(mut self, condition: bool, pipe: P) -> Self
where
P: Pipe<TPassable, TError> + Send + Sync + 'static,
{
if condition {
self.pipes.push(Arc::new(pipe));
}
self
}
pub fn unless<P>(mut self, condition: bool, pipe: P) -> Self
where
P: Pipe<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> Pipeline<TPassable, TError>
where
TError: Into<PipelineError>,
{
pub fn then<F, R>(self, destination: F) -> PipelineResult<R>
where
F: FnOnce(TPassable) -> R,
{
self.run(|passable| Ok(passable)).map(destination)
}
pub fn then_return(self) -> PipelineResult<TPassable> {
self.run(|passable| Ok(passable))
}
pub fn rescue<F>(self, recovery: F) -> PipelineResult<TPassable>
where
F: FnOnce(PipelineError) -> TPassable,
{
match self.then_return() {
| Ok(passable) => Ok(passable),
| Err(PipelineError::InputMissing) => Err(PipelineError::InputMissing),
| Err(err) => Ok(recovery(err)),
}
}
fn run<F>(self, destination: F) -> PipelineResult<TPassable>
where
F: Fn(TPassable) -> PipelineResult<TPassable, TError>,
{
let passable = self.passable.ok_or(PipelineError::InputMissing)?;
let next = Next::new(&self.pipes, &destination);
let result = next.handle(passable).map_err(Into::into);
if let Some(finally) = &self.finally {
finally(&result);
}
result
}
}
impl<TPassable, TError> Default for Pipeline<TPassable, TError> {
fn default() -> Self {
Self::new()
}
}