noema 0.4.0

Noema IOC and DI framework for Rust
Documentation
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

use super::{Input, InputHandler, NoemaResult};

/// Continuation passed to a pipeline behavior — invokes the rest of the chain.
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
    }
}

/// MediatR-style pipeline behavior: wrap the chain with optional short-circuit.
#[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
    })
}

/// Run global + per-input behaviors in order, then the handler.
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
}