Skip to main content

synaptic_runnables/
each.rs

1use async_trait::async_trait;
2use synaptic_core::{RunnableConfig, SynapticError};
3
4use crate::runnable::{BoxRunnable, Runnable};
5
6/// Maps a runnable over each element in a list input, producing a list of outputs.
7///
8/// Similar to Python's `map()`, this applies the inner runnable to every item
9/// in the input `Vec`, collecting the results into an output `Vec`.
10///
11/// ```ignore
12/// let upper = RunnableLambda::new(|s: String| async move {
13///     Ok(s.to_uppercase())
14/// });
15/// let each = RunnableEach::new(upper.boxed());
16/// let results = each.invoke(vec!["hello".into(), "world".into()], &config).await?;
17/// // results == vec!["HELLO", "WORLD"]
18/// ```
19pub struct RunnableEach<I: Send + 'static, O: Send + 'static> {
20    inner: BoxRunnable<I, O>,
21}
22
23impl<I: Send + 'static, O: Send + 'static> RunnableEach<I, O> {
24    pub fn new(inner: BoxRunnable<I, O>) -> Self {
25        Self { inner }
26    }
27}
28
29#[async_trait]
30impl<I: Send + 'static, O: Send + 'static> Runnable<Vec<I>, Vec<O>> for RunnableEach<I, O> {
31    async fn invoke(
32        &self,
33        input: Vec<I>,
34        config: &RunnableConfig,
35    ) -> Result<Vec<O>, SynapticError> {
36        let mut results = Vec::with_capacity(input.len());
37        for item in input {
38            results.push(self.inner.invoke(item, config).await?);
39        }
40        Ok(results)
41    }
42}