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
# curfew

[![Crates.io](https://img.shields.io/crates/v/curfew.svg)](https://crates.io/crates/curfew)
[![Documentation](https://docs.rs/curfew/badge.svg)](https://docs.rs/curfew)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

`curfew` is a deadline-aware fork-join helper for Rust. It lets you spin up work across
multiple threads, set an **absolute** deadline (`std::time::Instant`), and collect only the
results that arrive on time. Late workers are skipped without attempting to cancel them,
so real-time sections of your application can keep moving.

## Installation

Add the crate to your Cargo manifest:

```toml
[dependencies]
curfew = "0.0.5"
```

Enable the optional `tokio` feature to work with async tasks powered by Tokio.

## Usage

### Joining a group of tasks before a deadline

```rust
use curfew::{DeadlineJoiner, TaskOutcome};
use std::thread;
use std::time::{Duration, Instant};

fn main() {
    let deadline = Instant::now() + Duration::from_millis(200);
    let mut joiner = DeadlineJoiner::new(deadline);

    for job in 0..4 {
        joiner.spawn(move || {
            // staggered work with different run times
            thread::sleep(Duration::from_millis(40 * job));
            job * 10
        });
    }

    let summary = joiner.join_ready();
    println!("completed before deadline: {}", summary.completed_count());
    println!("missed deadline: {}", summary.timed_out_count());

    for task in summary.completed {
        match task.outcome {
            TaskOutcome::Completed(value) => println!("task {} => {value}", task.task_id),
            TaskOutcome::Panicked(info) => println!("task {} panicked: {}", task.task_id, info),
        }
    }
}
```

### Joining an individual handle with a deadline

```rust
use curfew::{join_with_deadline, JoinOutcome, TaskOutcome};
use std::thread;
use std::time::{Duration, Instant};

fn main() {
    let handle = thread::spawn(|| {
        thread::sleep(Duration::from_millis(150));
        99
    });

    match join_with_deadline(handle, Instant::now() + Duration::from_millis(80)) {
        JoinOutcome::Completed(result) => match result.outcome {
            TaskOutcome::Completed(value) => println!("finished on time: {value}"),
            TaskOutcome::Panicked(info) => println!("task panicked: {info}"),
        },
        JoinOutcome::TimedOut(late) => {
            println!("missed deadline; waiting asynchronously for the late result");
            let result = late.wait();
            println!("late completion: {:?}", result.outcome);
        }
    }
}
```

### Async tasks with Tokio (`tokio` feature)

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

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let deadline = Instant::now() + Duration::from_millis(120);
    let mut joiner = DeadlineJoinerAsync::new(deadline);

    for value in 0..3u32 {
        joiner.spawn(async move {
            tokio::time::sleep(Duration::from_millis(u64::from(30 * (value + 1))))
                .await;
            value
        });
    }

    let summary = joiner.join_ready().await;
    println!("async tasks finished on time: {}", summary.completed_count());

    let future = async {
        tokio::time::sleep(Duration::from_millis(200)).await;
        99
    };

    match join_with_deadline_async(future, Instant::now() + Duration::from_millis(60)).await {
        JoinOutcomeAsync::Completed(result) => match result.outcome {
            TaskOutcome::Completed(value) => println!("completed on time: {value}"),
            TaskOutcome::Panicked(info) => println!("task panicked: {info}"),
        },
        JoinOutcomeAsync::TimedOut(late) => {
            println!("future missed the deadline; waiting for late completion");
            let result = late.wait().await;
            println!("late outcome: {:?}", result.outcome);
        }
    }
}
```

## API Highlights

- `DeadlineJoiner` spawns tasks, tracks their start/finish instants, and only returns
  results that beat the deadline.
- `join_with_deadline` wraps a single `JoinHandle`, returning a `LateTask` so callers can
  optionally await the late result later.
- Enable the `tokio` feature for async siblings `DeadlineJoinerAsync` and
  `join_with_deadline_async` with the same semantics.
- Rich metadata via `TaskResult`, `TaskOutcome`, and `TimedOutTask` for diagnostics and
  logging.

## Limitations

- Tasks are never forcefully terminated. Anything that misses the deadline continues
  running in the background until it completes naturally.
- Deadlines rely on the monotonic `Instant` clock. System clock adjustments do not
  influence scheduling, but very long sleeps may still drift.

## Examples

Run the bundled examples with `cargo run --example basic` or see `examples/` for more
scenarios (enable `--features tokio` for `async_example`).

## Development

See [_dev/PRD.md](./_dev/PRD.md) for the full product specification and
[CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.

## License

This project is licensed under MIT. See [LICENSE](LICENSE) for details.