pool-mod 1.0.0

Generic object and connection pooling. Async-safe with min/max sizing, idle timeouts, max-lifetime enforcement, validation-on-borrow, and health-check callbacks. Works for database connections, HTTP clients, worker threads, or any expensive resource.
Documentation
# pool-mod v0.2.0 — Foundation

**The public API arrives.** v0.2.0 turns the scaffold into a working pool: a
[`Manager`] trait for the resource lifecycle, a thread-safe [`Pool`] with blocking
acquisition and timeouts, an RAII [`Pooled`] guard, configurable sizing and
expiry, and a generic [`Error`] type. It ships with unit tests for every lifecycle
path, an eight-thread end-to-end integration test, and a rustdoc example on every
public item — green across Linux, macOS, and Windows on both stable and the MSRV.
Zero dependencies.

## What is pool-mod?

A generic object and connection pool for Rust. Implement one trait describing how
to create, validate, and recycle a resource, and the pool handles sizing, blocking
acquisition with timeouts, validation-on-borrow, and idle/lifetime expiry. It is
runtime-agnostic and pulls in no third-party crates.

## What's new in 0.2.0

### `Manager` — the resource lifecycle contract

The single trait you implement. `create` builds a resource, `recycle` resets a
returned one, and the optional `validate` hook is the validation-on-borrow / health
check that runs on checkout. The pool never holds its internal lock while calling
these, so a slow constructor or validator does not stall other threads.

```rust
use pool_mod::Manager;
use std::convert::Infallible;

struct Buffers { capacity: usize }

impl Manager for Buffers {
    type Resource = Vec<u8>;
    type Error = Infallible;

    fn create(&self) -> Result<Vec<u8>, Infallible> {
        Ok(Vec::with_capacity(self.capacity))
    }
    fn recycle(&self, buf: &mut Vec<u8>) -> Result<(), Infallible> {
        buf.clear();
        Ok(())
    }
}
```

### `Pool` — the thread-safe pool

`Pool<M>` is `Send + Sync` and cheap to clone; each clone is another handle onto
the same shared pool. `get` borrows a resource, waiting up to the configured
`create_timeout`; `get_timeout` overrides the wait per call, and `Duration::ZERO`
turns it into a non-blocking try. `status`, `close`, and `is_closed` round out the
surface.

```rust
use pool_mod::{Manager, Pool};
# use std::convert::Infallible;
# struct Buffers { capacity: usize }
# impl Manager for Buffers {
#   type Resource = Vec<u8>; type Error = Infallible;
#   fn create(&self) -> Result<Vec<u8>, Infallible> { Ok(Vec::with_capacity(self.capacity)) }
#   fn recycle(&self, b: &mut Vec<u8>) -> Result<(), Infallible> { b.clear(); Ok(()) }
# }
let pool = Pool::builder(Buffers { capacity: 4096 })
    .max_size(16)
    .min_idle(4)
    .build()
    .expect("configuration is valid");

let mut buf = pool.get().expect("a buffer is available");
buf.extend_from_slice(b"payload");
assert_eq!(pool.status().in_use, 1);
```

The acquisition loop reserves a slot, releases the lock, and only then runs the
manager's `create` / `validate` / `recycle`. The mutex guards just a small queue
and two counters; resource construction and user code always run unlocked.

### `Pooled` — RAII return

The guard returned by `get` deref-coerces to the resource and returns it to the
pool when dropped — recycled on the way back in. There is no `release` to forget,
and forgetting a guard cannot leak a resource. If `recycle` fails or the pool is
closed, the resource is dropped and its slot freed for a replacement. The guard is
`Send`, so it can be held across `.await` points.

The guard owns its resource through `ManuallyDrop` and moves it out exactly once,
in `Drop`. That is the crate's only `unsafe` block; it carries a `// SAFETY:`
proof and is covered by the check-in/return tests.

### `Builder` and `PoolConfig` — configuration

`PoolConfig` exposes `max_size`, `min_idle`, `create_timeout`, `idle_timeout`, and
`max_lifetime` as public fields with documented defaults (max 10, 30s create
timeout, no expiry). `Builder` is the fluent path; `build` validates the config —
rejecting a zero `max_size` or a `min_idle` above `max_size` with
`Error::InvalidConfig` — and pre-creates the `min_idle` resources.

### `Error` — generic over the manager's error

`Error<E>` carries the manager's own error unchanged in `Backend(E)`, alongside
`Timeout`, `Closed`, and `InvalidConfig`. It is `#[non_exhaustive]`, implements
`Display`, and implements `std::error::Error` (with `source()`) whenever `E` does.

### Lifecycle enforcement

Borrowing applies the full policy before handing a resource back: `max_lifetime`
and `idle_timeout` are checked against the resource's timestamps, then `validate`
runs. A resource that is too old, too stale, or invalid is dropped and the pool
supplies a replacement — the caller only ever receives a live resource.

### Documentation

`docs/API.md` now documents every public item with parameters, return values,
error semantics, and at least one runnable example, plus a Patterns section
(database connections, validation-on-borrow, cross-thread sharing, async usage).
The README is filled out with features, configuration, and a how-it-works section.

## Breaking changes

**None of consequence.** v0.1.0 had no public API beyond `VERSION`, which is
unchanged. Everything in this release is additive.

## Verification

Run on Windows x86_64 (Rust stable 1.95.0 and 1.75.0) and on Linux via WSL2
Ubuntu (Rust stable 1.95.0); these mirror the CI matrix.

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --no-default-features --all-targets -- -D warnings
cargo test --all-features
cargo build --no-default-features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo +1.75 build --all-features
cargo +1.75 test --all-features
```

All green. Counts at this tag: 18 unit tests, 3 integration tests, 1 smoke test,
and 16 doctests.

## What's next

- **0.5.0 — Implementation.** Property tests for the pool's invariants, broader
  integration tests, Criterion benchmarks with tracked baselines, a background
  idle reaper, and an opt-in native async acquisition API.

## Installation

```toml
[dependencies]
pool-mod = "0.2"
```

MSRV: Rust 1.75. Edition 2021.

## Documentation

- [README]https://github.com/jamesgober/pool-mod/blob/main/README.md
- [API Reference]https://github.com/jamesgober/pool-mod/blob/main/docs/API.md
- [CHANGELOG]https://github.com/jamesgober/pool-mod/blob/main/CHANGELOG.md

---

**Full diff:** [`v0.1.0...v0.2.0`](https://github.com/jamesgober/pool-mod/compare/v0.1.0...v0.2.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/pool-mod/blob/main/CHANGELOG.md#020---2026-05-27).

[`Manager`]: https://docs.rs/pool-mod/0.2.0/pool_mod/trait.Manager.html
[`Pool`]: https://docs.rs/pool-mod/0.2.0/pool_mod/struct.Pool.html
[`Pooled`]: https://docs.rs/pool-mod/0.2.0/pool_mod/struct.Pooled.html
[`Error`]: https://docs.rs/pool-mod/0.2.0/pool_mod/enum.Error.html