Skip to main content

entelix_runnable/
mapping.rs

1//! `Mapping<R, F>` — `Runnable<I, P>` adapter that pipes the inner's
2//! output through a synchronous `Fn(O) -> P`.
3//!
4//! Equivalent to `inner.pipe(RunnableLambda::new(|o, _| async move {
5//! Ok(f(o)) }))` but avoids allocating a wrapping `RunnableLambda` and
6//! keeps the closure non-async at the type level.
7
8use std::marker::PhantomData;
9use std::sync::Arc;
10
11use async_trait::async_trait;
12
13use entelix_core::ExecutionContext;
14use entelix_core::error::Result;
15
16use crate::runnable::Runnable;
17
18/// `Runnable<I, P>` adapter applying a pure synchronous function to
19/// the inner's output.
20pub struct Mapping<R, F, I, O, P>
21where
22    R: Runnable<I, O> + 'static,
23    F: Fn(O) -> P + Send + Sync + 'static,
24    I: Send + 'static,
25    O: Send + 'static,
26    P: Send + 'static,
27{
28    inner: Arc<R>,
29    f: Arc<F>,
30    _io: PhantomData<fn(I) -> (O, P)>,
31}
32
33impl<R, F, I, O, P> Mapping<R, F, I, O, P>
34where
35    R: Runnable<I, O> + 'static,
36    F: Fn(O) -> P + Send + Sync + 'static,
37    I: Send + 'static,
38    O: Send + 'static,
39    P: Send + 'static,
40{
41    /// Build with the inner runnable and a pure mapping function.
42    pub fn new(inner: R, f: F) -> Self {
43        Self {
44            inner: Arc::new(inner),
45            f: Arc::new(f),
46            _io: PhantomData,
47        }
48    }
49}
50
51impl<R, F, I, O, P> std::fmt::Debug for Mapping<R, F, I, O, P>
52where
53    R: Runnable<I, O> + 'static,
54    F: Fn(O) -> P + Send + Sync + 'static,
55    I: Send + 'static,
56    O: Send + 'static,
57    P: Send + 'static,
58{
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.debug_struct("Mapping").finish_non_exhaustive()
61    }
62}
63
64#[async_trait]
65impl<R, F, I, O, P> Runnable<I, P> for Mapping<R, F, I, O, P>
66where
67    R: Runnable<I, O> + 'static,
68    F: Fn(O) -> P + Send + Sync + 'static,
69    I: Send + 'static,
70    O: Send + 'static,
71    P: Send + 'static,
72{
73    async fn invoke(&self, input: I, ctx: &ExecutionContext) -> Result<P> {
74        let value = self.inner.invoke(input, ctx).await?;
75        Ok((self.f)(value))
76    }
77}