curfew 0.0.5

A deadline-aware fork-join model where multiple tasks run in parallel, but only those that finish before a specified absolute clock time are joined and their results collected.
Documentation
#![cfg(feature = "tokio")]

use curfew::{
    join_with_deadline_async, DeadlineJoinerAsync, JoinOutcomeAsync,
    TaskOutcome,
};
use std::time::{Duration, Instant};

#[tokio::test]
async fn async_joiner_collects_on_time() {
    let deadline = Instant::now() + Duration::from_millis(150);
    let mut joiner = DeadlineJoinerAsync::new(deadline);

    for value in 0..3u32 {
        joiner.spawn(async move {
            tokio::time::sleep(Duration::from_millis(20)).await;
            value * 2
        });
    }

    let summary = joiner.join_ready().await;
    assert_eq!(summary.completed_count(), 3);
    assert_eq!(summary.timed_out_count(), 0);
}

#[tokio::test]
async fn async_joiner_respects_deadline() {
    let deadline = Instant::now() + Duration::from_millis(80);
    let mut joiner = DeadlineJoinerAsync::new(deadline);

    joiner.spawn(async {
        tokio::time::sleep(Duration::from_millis(30)).await;
        1
    });
    joiner.spawn(async {
        tokio::time::sleep(Duration::from_millis(150)).await;
        2
    });

    let summary = joiner.join_ready().await;
    assert_eq!(summary.completed_count(), 1);
    assert_eq!(summary.timed_out_count(), 1);
}

#[tokio::test]
async fn join_with_deadline_async_reports_timeout() {
    let future = async {
        tokio::time::sleep(Duration::from_millis(120)).await;
        42
    };

    let deadline = Instant::now() + Duration::from_millis(40);
    match join_with_deadline_async(future, deadline).await {
        JoinOutcomeAsync::Completed(_) => {
            panic!("future should not complete before deadline")
        }
        JoinOutcomeAsync::TimedOut(late) => {
            let result = late.wait().await;
            match result.outcome {
                TaskOutcome::Completed(value) => assert_eq!(value, 42),
                TaskOutcome::Panicked(info) => {
                    panic!("unexpected panic: {info}")
                }
            }
        }
    }
}