ps-promise 0.1.0-17

Promise-like owned futures
Documentation
# ps-promise

ECMAScript-style promises for Rust: owned futures with typed rejections.

A `Promise<T, E>` is a `Future` with `Output = Result<T, E>` that owns its
computation and remembers its outcome. It can be inspected without consuming
the result (`peek`, `is_resolved`, `is_rejected`), polled manually (`poll`,
`poll_sync`), consumed (`consume`), or awaited. Awaiting consumes the result;
consuming a second time yields a synthesized `already_consumed` rejection,
except for task failures, which replay through `task_failed` on every
consumption.
`Promise::shared` produces a handle that, like an ECMAScript promise, can be
awaited any number of times.

## Example

```rust
use ps_promise::{Promise, PromiseRejection, TaskFailure};

#[derive(Debug)]
enum Error {
    AlreadyConsumed,
    TaskFailed(TaskFailure),
}

impl PromiseRejection for Error {
    fn already_consumed() -> Self {
        Self::AlreadyConsumed
    }

    fn task_failed(failure: TaskFailure) -> Self {
        Self::TaskFailed(failure)
    }
}

async fn demo() -> Result<(), Error> {
    let promise: Promise<i32, Error> =
        Promise::lazy(async { Ok(21) }).map(|n| async move { n * 2 });

    assert_eq!(promise.await?, 42);

    Ok(())
}
```

## ECMAScript correspondence

| ECMAScript                | ps-promise                                   |
| ------------------------- | -------------------------------------------- |
| `Promise.resolve(value)`  | `Promise::resolve(value)`                    |
| `Promise.reject(error)`   | `Promise::reject(error)`                     |
| `new Promise(executor)`   | `Promise::new(executor)`                     |
| `Promise.withResolvers()` | `Promise::with_resolvers()`                  |
| `Promise.try(func)`       | `Promise::attempt`, `Promise::attempt_async` |
| `Promise.all(promises)`   | `Promise::all(promises)`                     |
| `Promise.any(promises)`   | `Promise::any(promises)`                     |
| `Promise.race(promises)`  | `Promise::race(promises)`                    |
| `Promise.allSettled(ps)`  | `Promise::all_settled(ps)`                   |
| `promise.then(f)`         | `promise.then(f)`, `promise.map(f)`          |
| `promise.then(f, g)`      | `promise.then_catch(f, g)`                   |
| `promise.catch(f)`        | `promise.catch(f)`, `promise.map_err(f)`     |
| `promise.finally(f)`      | `promise.finally(f)`                         |
| thenable assimilation     | `promise.flatten()`                          |
| awaiting more than once   | `promise.shared()`                           |

Beyond the ECMAScript surface, the crate provides the scheduling
constructors `lazy`, `eager`, and `eager_or_lazy`, plus `timeout`, `sleep`,
`abortable`, `detach`, `unblock` (offloading blocking work), `wrap`, `zip`,
`inspect`, and `inspect_err`. `eager` and `detach` exist only when a runtime
feature (`tokio` or `smol`) is enabled.

## Rejections

The rejection type implements the `PromiseRejection` trait, which lets a
promise synthesize rejections in two cases no user value covers: consumption
after the result was already taken, and failure of the underlying task,
e.g. a panic (caught inside the promise body rather than unwinding into the
caller) or a cancellation. Escape hatches exist for error types that do not
model these cases: `()` discards all information, `Vec<E>` lifts rejections
element-wise, `WrappedPromiseRejection<E>` wraps an arbitrary error type,
and the `anyhow` feature implements the trait for `anyhow::Error`.

## Scheduling and Cargo features

Without a runtime feature, every promise is lazy: the wrapped future
progresses only while the promise is polled, and dropping the promise drops
the future. Enabling a runtime feature makes `Promise::eager_or_lazy`, and
every combinator built on it, spawn the future instead (falling back to lazy
when only `tokio` is enabled and no runtime context is active); a spawned
future runs to completion even if the promise is dropped. Because Cargo unifies
features across the whole build graph, any dependency enabling a runtime
feature flips this behavior for every crate in the build; when laziness is
required for correctness, construct the promise with `Promise::lazy`.

- `tokio`: eager scheduling via `tokio::spawn` and tokio-backed timers.
- `smol`: eager scheduling via `smol::spawn` and smol-backed timers.
- `anyhow`: implements `PromiseRejection` for `anyhow::Error`.

## License

GPL-3.0-or-later