effect-rs 0.1.0

A high-performance, strictly-typed, functional effect system for Rust.
Documentation
use effect_rs::{Effect, Exit, Schedule, TestRuntime};
use std::sync::{Arc, Mutex};
use std::time::Duration;

#[test]
fn test_virtual_clock() {
    let rt = TestRuntime::new();
    let duration = Duration::from_secs(10);

    // Program: Sleep 10s then return 42.
    let program: Effect<(), (), i32> = Effect::<(), (), ()>::sleep(duration).map(|_| 42);

    // Spawn the effect
    let handle = rt.spawn(program, ());

    // Allow the spawned task to run and register the sleeper
    std::thread::sleep(Duration::from_millis(100));

    // Advance time by 10s (blocking)
    rt.advance_blocking(Duration::from_secs(10));

    // Wait for result
    let result = rt.block_on_future(handle).unwrap();

    match result {
        Exit::Success(val) => assert_eq!(val, 42),
        Exit::Failure(_) => panic!("Expected success"),
    }
}

#[test]
fn test_retry_virtual_time() {
    let rt = TestRuntime::new();

    let counter = Arc::new(Mutex::new(0));
    let counter_clone = counter.clone();

    // Program: Fail 2 times, then succeed.
    // Retry policy: spaced 5s.
    // Total time: 0 fail -> wait 5s -> 1 fail -> wait 5s -> 2 succeed. Total 10s.
    // Actually spaced 5s means:
    // Try 1 (fail) -> wait 5s -> Try 2 (fail) -> wait 5s -> Try 3 (success).

    let program = Effect::sync(move || {
        let mut c = counter_clone.lock().unwrap();
        *c += 1;
        if *c <= 2 { Err(()) } else { Ok(42) }
    })
    .flat_map(|res| match res {
        Ok(v) => Effect::succeed(v),
        Err(_) => Effect::fail(()),
    })
    .retry(Schedule::<(), ()>::spaced(Duration::from_secs(5)));

    let handle = rt.spawn(program, ());

    // After 100ms, it should run once, fail, and be sleeping for 5s.
    std::thread::sleep(Duration::from_millis(100));
    assert_eq!(*counter.lock().unwrap(), 1);

    // Advance 5s.
    rt.advance_blocking(Duration::from_secs(5));
    // It should run again (2), fail, and sleep 5s.
    std::thread::sleep(Duration::from_millis(100)); // Allow retry loop to run
    assert_eq!(*counter.lock().unwrap(), 2);

    // Advance 5s.
    rt.advance_blocking(Duration::from_secs(5));
    // It should run again (3), succeed.

    let result = rt.block_on_future(handle).unwrap();
    match result {
        Exit::Success(val) => assert_eq!(val, 42),
        Exit::Failure(_) => panic!("Expected success"),
    }

    assert_eq!(*counter.lock().unwrap(), 3);
}