# cache-mod v0.2.0 — Foundation: public API surface

**The first release with actual code behind the front door.** `0.1.0` was the scaffold; this release defines the public API surface the rest of the road to 1.0 builds on. Three items land: the [`Cache`] trait, the [`LruCache`] reference implementation, and the [`CacheError`] type. The eviction-policy story (LFU, TinyLFU, TTL, size-bounded) plays out across the upcoming `0.3.0 — 0.5.0` minors; everything in 0.2.0 is shaped so those additions are purely additive.

## What is cache-mod?

High-performance in-process caching for Rust with multiple eviction policies planned (LRU, LFU, TinyLFU, TTL, size-bounded). Async-safe (`&self` everywhere, `Send + Sync` cache instances), lock-minimized internals. Typed key-value API. No dependency on any external store.

## What's in 0.2.0

The foundation milestone delivers four things:

- **The `Cache<K, V>` trait.** The common read / write / evict contract every cache type in this crate will implement. Methods: `get`, `insert`, `remove`, `contains_key`, `len`, `is_empty`, `clear`, `capacity`. Every method takes `&self` — so a single cache instance can be shared across threads and across `.await` points without external locking. Eviction-policy semantics are documented at the trait level: `get` and `insert` count as accesses (and may promote within the policy); `contains_key` is a query-only operation that explicitly does **not** disturb access order. `is_empty` has a default impl on top of `len`.
- **`LruCache<K, V>` — the reference Least-Recently-Used implementation.** Bounded capacity, thread-safe via `std::sync::Mutex<Inner>` over `{ HashMap, VecDeque }`. On insert overflow the least-recently-accessed entry is evicted. `get` / `insert` promote to MRU; `contains_key` does not. Two constructors: `LruCache::new(usize) -> Result<Self, CacheError>` (fallible, rejects zero) and `LruCache::with_capacity(NonZeroUsize) -> Self` (infallible). Poison-tolerant: a panic inside a `get` or `insert` does not brick the cache — every subsequent call recovers the guard and re-establishes the `map`/`order` invariant. Marked as the **reference implementation**: correctness over speed at this milestone. The lock-free, arena-backed replacement that lands in 0.5.0 keeps this same public surface.
- **`CacheError`, `#[non_exhaustive]`.** Single variant for now (`InvalidCapacity`). Implements `Debug`, `Clone`, `Copy`, `PartialEq`, `Eq`, `Hash`, `Display`. `std::error::Error` is implemented under `#[cfg(feature = "std")]` so the error type stays no_std-compatible for forward use.
- **End-to-end smoke tests.** 11 integration tests in `tests/smoke.rs` plus 6 doctests covering: zero-capacity rejection, insert / get round-trip, replacement-returns-old, LRU eviction order under capacity pressure, `contains_key` not promoting, removal returning the value, `clear` preserving capacity, capacity reporting, `Send + Sync` static-assertion, and `Display` content for the error.

## What's not in 0.2.0

By design, deferred to subsequent minors:

- **LFU, TinyLFU, TTL, size-bounded caches.** Each is a separate type implementing `Cache<K, V>`. Landing one-per-minor through 0.5.0.
- **`Borrow<Q>`-generic lookups** (e.g. `get(&"key")` against a `Cache<String, _>` without an intermediate `String`). Additive — no breaking change when introduced.
- **Lock-free internals.** The `Mutex` in 0.2.0's `LruCache` is the obvious target for 0.5.0's hardening pass. The public API does not expose the lock.
- **Async-runtime helpers.** Out of scope per the crate's stated discipline (no async-runtime hard dependency).
- **`no_std` `LruCache`.** The trait + error type stay no_std-compatible today; the `LruCache` impl is gated on `feature = "std"` because it uses `HashMap` / `Mutex`. A `hashbrown`-backed no_std variant is a candidate for a later minor if demand surfaces.

## Breaking changes

**None** — the entire public surface introduced in 0.2.0 is new. There are no `0.1.0 → 0.2.0` call-sites in the wild to break (0.1.0 exposed only `pub const VERSION`).

The crate remains pre-1.0; minor versions may break in the future. Pin exact versions and read the changelog.

## Carried-forward fixes from 0.1.0

Per release policy, baseline CI failures present at the 0.1.0 scaffold tag were not patched in 0.1.0 — they landed here:

- `Cargo.toml` edition was lowered from `2024` to `2021`. Edition 2024 requires Rust 1.85+ and the declared MSRV is 1.75 — the mismatch made `cargo` refuse to even parse the manifest on the MSRV CI row. Synced `rustfmt.toml` edition for consistency.
- Added `.gitattributes` enforcing `eol=lf` across the tree. Windows GitHub Actions runners default to `core.autocrlf=true`, which converted the repo's LF-line-ended files to CRLF on checkout; `rustfmt.toml` has `newline_style = "Unix"`, so rustfmt then rejected every `.rs` file on the Windows matrix.
- Stripped UTF-8 BOMs (`\xef\xbb\xbf`) from every scaffold-generated text file. Cargo tolerates a leading BOM in `Cargo.toml`; `Swatinem/rust-cache@v2` does not, and was logging `Error parsing Cargo.toml manifest, fallback to caching entire file` on every CI run — losing per-package cache scoping silently.
- CI workflow: dropped the unused `actions/setup-node@v5` step (cache-mod is pure Rust); switched the dependency cache from `actions/cache@v4` to `Swatinem/rust-cache@v2` to match the rest of the crate family; added `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true` at the workflow level to opt every JavaScript action into Node.js 24 ahead of the June 2, 2026 forced migration.

## Verification

Local run on Windows x86_64, Rust stable; the same commands run green across the configured CI matrix (Linux / macOS / Windows on stable + MSRV 1.75):

```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --all-targets --no-default-features -- -D warnings
cargo test --all-features
cargo doc --no-deps --all-features
```

All green. Test totals:

- Integration tests: **11 passed**.
- Doctests: **6 passed**.

REPS lint surface (declared in `src/lib.rs`) is honored: `deny(missing_docs)`, `deny(unsafe_op_in_unsafe_fn)`, `deny(unused_must_use)`, `deny(unused_results)`, `deny(clippy::unwrap_used)`, `deny(clippy::expect_used)`, `deny(clippy::todo)`, `deny(clippy::unimplemented)`, `deny(clippy::print_stdout)`, `deny(clippy::print_stderr)`, `deny(clippy::dbg_macro)`, `deny(clippy::undocumented_unsafe_blocks)`, `deny(clippy::missing_safety_doc)`. No `unsafe` is used anywhere in the crate at this milestone.

## What's next

`0.3.0 — 0.4.0` add the remaining eviction policies (LFU / TinyLFU / TTL / size-bounded) one per minor. `0.5.0` is the implementation-quality milestone: the `Mutex`-guarded `LruCache` is replaced by a lock-free, arena-backed implementation while keeping the public surface identical; property tests on cache invariants land here; basic benchmarks land here. `0.9.0` is the hardening + audit pass; `1.0.0` is the API freeze.

## Installation

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

MSRV: Rust 1.75. Edition 2021. `default-features = ["std"]`.

```rust
use cache_mod::{Cache, LruCache};

let cache: LruCache<&'static str, u32> = LruCache::new(64).expect("capacity > 0");

cache.insert("requests", 1);
cache.insert("errors", 0);

assert_eq!(cache.get(&"requests"), Some(1));
```

## Documentation

- [README](https://github.com/jamesgober/cache-mod/blob/main/README.md)
- [CHANGELOG](https://github.com/jamesgober/cache-mod/blob/main/CHANGELOG.md)
- [REPS standards](https://github.com/jamesgober/cache-mod/blob/main/REPS.md)
- [API reference](https://docs.rs/cache-mod/0.2.0)

---

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

[`Cache`]: https://docs.rs/cache-mod/0.2.0/cache_mod/trait.Cache.html
[`LruCache`]: https://docs.rs/cache-mod/0.2.0/cache_mod/struct.LruCache.html
[`CacheError`]: https://docs.rs/cache-mod/0.2.0/cache_mod/enum.CacheError.html
