effect-rs 0.1.0

A high-performance, strictly-typed, functional effect system for Rust.
Documentation
use effect_rs::{Effect, Exit, Runtime};
use std::time::{Duration, Instant};

#[test]
fn test_zip_par_success() {
    let rt = Runtime::new();

    let start = Instant::now();
    let program: Effect<(), (), (i32, i32)> = Effect::<(), (), i32>::async_effect(|| async {
        tokio::time::sleep(Duration::from_millis(100)).await;
        10
    })
    .zip_par(Effect::<(), (), i32>::async_effect(|| async {
        tokio::time::sleep(Duration::from_millis(100)).await;
        20
    }));

    let result = rt.block_on(program, ());
    let duration = start.elapsed();

    match result {
        Exit::Success((a, b)) => {
            assert_eq!(a, 10);
            assert_eq!(b, 20);
            // Should run in parallel, so duration should be close to 100ms, definitely < 150ms
            assert!(
                duration < Duration::from_millis(180),
                "Took too long: {:?}",
                duration
            );
        }
        Exit::Failure(_) => panic!("Expected success"),
    }
}

#[test]
fn test_zip_par_failure() {
    let rt = Runtime::new();

    let program: Effect<(), &'static str, (i32, i32)> =
        Effect::fail("error").zip_par(Effect::async_effect(|| async {
            tokio::time::sleep(Duration::from_millis(200)).await;
            20
        }));

    let start = Instant::now();
    let result = rt.block_on(program, ());
    let duration = start.elapsed();

    match result {
        Exit::Success(_) => panic!("Expected failure"),
        Exit::Failure(cause) => match cause {
            effect_rs::Cause::Fail(e) => assert_eq!(e, "error"),
            _ => panic!("Expected Fail cause, got {:?}", cause),
        },
    }
    // Should fail fast
    assert!(
        duration < Duration::from_millis(100),
        "Did not fail fast: {:?}",
        duration
    );
}

#[test]
fn test_race_winner() {
    let rt = Runtime::new();

    // Effect 1: Sleeps 100ms then succeeds with 1
    let e1: Effect<(), (), i32> = Effect::async_effect(|| async {
        tokio::time::sleep(std::time::Duration::from_millis(500)).await;
        1
    });

    // Effect 2: Succeeds immediately with 2
    let e2: Effect<(), (), i32> = Effect::succeed(2);

    let program = e1.race(e2);
    let result = rt.block_on(program, ());
    match result {
        Exit::Success(val) => assert_eq!(val, 2),
        Exit::Failure(_) => panic!("Expected success"),
    }
}

#[test]
fn test_collect_all_par() {
    let rt = Runtime::new();

    let effects = vec![
        Effect::<(), (), i32>::succeed(1),
        Effect::<(), (), i32>::succeed(2),
        Effect::<(), (), i32>::succeed(3),
    ];

    let program = Effect::collect_all_par(effects);
    let result = rt.block_on(program, ());
    match result {
        Exit::Success(vec) => assert_eq!(vec, vec![1, 2, 3]),
        Exit::Failure(_) => panic!("Expected success"),
    }
}