Skip to main content

entelix_runnable/
configured.rs

1//! `Configured<R, F>` — `Runnable<I, O>` adapter that mutates a
2//! cloned [`ExecutionContext`] via a caller-supplied closure before
3//! delegating to the inner runnable.
4//!
5//! Useful for branch-local context overrides (a sub-tree using a
6//! different `tenant_id`, a stricter `deadline`, or a fresh
7//! cancellation scope) without leaking those changes back to the
8//! caller's context.
9
10use std::marker::PhantomData;
11use std::sync::Arc;
12
13use async_trait::async_trait;
14
15use entelix_core::ExecutionContext;
16use entelix_core::error::Result;
17
18use crate::runnable::Runnable;
19
20/// `Runnable<I, O>` adapter that runs `configurer` on a cloned
21/// `ExecutionContext` before forwarding to the inner. The original
22/// `ctx` the parent passed in is left untouched.
23pub struct Configured<R, F, I, O>
24where
25    R: Runnable<I, O> + 'static,
26    F: Fn(&mut ExecutionContext) + Send + Sync + 'static,
27    I: Send + 'static,
28    O: Send + 'static,
29{
30    inner: Arc<R>,
31    configurer: Arc<F>,
32    _io: PhantomData<fn(I) -> O>,
33}
34
35impl<R, F, I, O> Configured<R, F, I, O>
36where
37    R: Runnable<I, O> + 'static,
38    F: Fn(&mut ExecutionContext) + Send + Sync + 'static,
39    I: Send + 'static,
40    O: Send + 'static,
41{
42    /// Build with the inner runnable and a context-mutating closure.
43    pub fn new(inner: R, configurer: F) -> Self {
44        Self {
45            inner: Arc::new(inner),
46            configurer: Arc::new(configurer),
47            _io: PhantomData,
48        }
49    }
50}
51
52impl<R, F, I, O> std::fmt::Debug for Configured<R, F, I, O>
53where
54    R: Runnable<I, O> + 'static,
55    F: Fn(&mut ExecutionContext) + Send + Sync + 'static,
56    I: Send + 'static,
57    O: Send + 'static,
58{
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.debug_struct("Configured").finish_non_exhaustive()
61    }
62}
63
64#[async_trait]
65impl<R, F, I, O> Runnable<I, O> for Configured<R, F, I, O>
66where
67    R: Runnable<I, O> + 'static,
68    F: Fn(&mut ExecutionContext) + Send + Sync + 'static,
69    I: Send + 'static,
70    O: Send + 'static,
71{
72    async fn invoke(&self, input: I, ctx: &ExecutionContext) -> Result<O> {
73        let mut scoped = ctx.clone();
74        (self.configurer)(&mut scoped);
75        self.inner.invoke(input, &scoped).await
76    }
77}