effect-rs 0.1.0

A high-performance, strictly-typed, functional effect system for Rust.
Documentation
use crate::core::*;
use std::marker::PhantomData;
use std::sync::Arc;

/// Layer<In, E, Out>
/// Builds an Env containing Out, starting from In.
///
/// In: The environment required to build the layer
/// E: The error type
/// Out: The environment provided by the layer
pub struct Layer<In, E, Out> {
    pub build: Effect<In, E, Env>,
    _phantom: PhantomData<Out>,
}

impl<In, E, Out> Clone for Layer<In, E, Out> {
    fn clone(&self) -> Self {
        Self {
            build: self.build.clone(),
            _phantom: PhantomData,
        }
    }
}

impl<In, E, Out> Layer<In, E, Out>
where
    In: Clone + Send + Sync + 'static,
    E: Send + Sync + 'static,
    Out: Send + Sync + 'static,
{
    /// Creates a layer from an effect that builds the environment.
    pub fn new(build: Effect<In, E, Env>) -> Self {
        Self {
            build,
            _phantom: PhantomData,
        }
    }

    /// Creates a layer that provides a single service.
    pub fn succeed<T>(val: T) -> Layer<In, E, T>
    where
        T: Send + Sync + Clone + 'static,
        In: Send + Sync, // In is ignored
        E: Send + Sync,
    {
        Layer {
            build: Effect::sync(move || {
                let mut env = Env::new();
                env.insert(val.clone());
                env
            }),
            _phantom: PhantomData,
        }
    }

    // Provide this layer to an effect
    pub fn provide_to<A>(self, effect: Effect<Env, E, A>) -> Effect<In, E, A>
    // Effect accepts Env, but we return Effect<In...>
    where
        A: Send + Sync + 'static,
        // E, In, Out bound by impl
    {
        // 1. Build the environment (consumes In)
        // 2. Run the effect with that environment
        self.build.flat_map(move |env| {
            let provided = effect.clone().provide(env);
            // Manually construct effect to propagate Exit correctly
            Effect {
                inner: Arc::new(move |_env: EnvRef<In>, ctx| {
                    let provided = provided.clone();
                    Box::pin(async move { (provided.inner)(EnvRef { value: () }, ctx).await })
                }),
            }
        })
    }
}