# pending-requests
Track in-flight requests and await their responses by key.
A `PendingRequests` registry lets one task register a request under a key, hand
back a future, and have another task deliver the matching response later. This
is the classic pattern behind request/response protocols multiplexed over a
single connection (request id in, response with the same id out).
## Features
- Await a response by key with a per-registry or per-request timeout.
- Cheap to clone: every clone shares the same pending set, so a reader task can
deliver responses while many sender tasks await theirs.
- Precise outcomes: a response, a timeout, or a cancellation are distinct errors.
- No leaks: entries are removed on response, timeout, cancellation, or when the
waiter is dropped.
## Example
```rust
use std::time::Duration;
use pending_requests::PendingRequests;
#[tokio::main]
async fn main() {
let requests = PendingRequests::<u64, String>::new();
// Register a request keyed by its id.
let waiter = requests.prepare_response(1).unwrap();
// Elsewhere (e.g. a socket reader task), deliver the response:
let responder = requests.clone();
responder.handle_response(1, "pong".to_string()).unwrap();
assert_eq!(waiter.await.unwrap(), "pong");
}
```
## API overview
| `prepare_response(key)` | Register a request, get a `ResponseWaiter` future. |
| `prepare_response_with_timeout(key, dur)` | Same, with a per-request timeout. |
| `handle_response(key, resp)` | Deliver a response to a waiter. |
| `cancel(&key)` | Cancel one request (waiter resolves with `Canceled`). |
| `cancel_all()` | Cancel every pending request; returns the count. |
| `contains(&key)` | Whether a request is pending for `key`. |
| `set_timeout(dur)` / `timeout()` | Get/set the default timeout (works through a clone). |
| `len()` / `is_empty()` | Number of pending requests. |
A `ResponseWaiter` resolves to:
- `Ok(response)` — the response was delivered,
- `Err(RequestTimeout)` — the timeout elapsed first,
- `Err(Canceled)` — the request was canceled or the registry was fully dropped.
## Timeout cleanup
A request's timer lives inside its `ResponseWaiter`; there is no background
task per request. Cleanup of a timed-out entry happens when the waiter is
polled or dropped. In normal usage you always `.await` the waiter, so this is
automatic. If you register a request and then neither poll nor drop the waiter,
its entry stays until you do.
## License
Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or
[MIT license](LICENSE-MIT) at your option.