effect_rs/
layer.rs

1use crate::core::*;
2use std::marker::PhantomData;
3use std::sync::Arc;
4
5/// Layer<In, E, Out>
6/// Builds an Env containing Out, starting from In.
7///
8/// In: The environment required to build the layer
9/// E: The error type
10/// Out: The environment provided by the layer
11pub struct Layer<In, E, Out> {
12    pub build: Effect<In, E, Env>,
13    _phantom: PhantomData<Out>,
14}
15
16impl<In, E, Out> Clone for Layer<In, E, Out> {
17    fn clone(&self) -> Self {
18        Self {
19            build: self.build.clone(),
20            _phantom: PhantomData,
21        }
22    }
23}
24
25impl<In, E, Out> Layer<In, E, Out>
26where
27    In: Clone + Send + Sync + 'static,
28    E: Send + Sync + 'static,
29    Out: Send + Sync + 'static,
30{
31    /// Creates a layer from an effect that builds the environment.
32    pub fn new(build: Effect<In, E, Env>) -> Self {
33        Self {
34            build,
35            _phantom: PhantomData,
36        }
37    }
38
39    /// Creates a layer that provides a single service.
40    pub fn succeed<T>(val: T) -> Layer<In, E, T>
41    where
42        T: Send + Sync + Clone + 'static,
43        In: Send + Sync, // In is ignored
44        E: Send + Sync,
45    {
46        Layer {
47            build: Effect::sync(move || {
48                let mut env = Env::new();
49                env.insert(val.clone());
50                env
51            }),
52            _phantom: PhantomData,
53        }
54    }
55
56    // Provide this layer to an effect
57    pub fn provide_to<A>(self, effect: Effect<Env, E, A>) -> Effect<In, E, A>
58    // Effect accepts Env, but we return Effect<In...>
59    where
60        A: Send + Sync + 'static,
61        // E, In, Out bound by impl
62    {
63        // 1. Build the environment (consumes In)
64        // 2. Run the effect with that environment
65        self.build.flat_map(move |env| {
66            let provided = effect.clone().provide(env);
67            // Manually construct effect to propagate Exit correctly
68            Effect {
69                inner: Arc::new(move |_env: EnvRef<In>, ctx| {
70                    let provided = provided.clone();
71                    Box::pin(async move { (provided.inner)(EnvRef { value: () }, ctx).await })
72                }),
73            }
74        })
75    }
76}