cached 3.1.1

Generic cache implementations and simplified function memoization
Documentation
[package]
name = "cached"
version = "3.1.1"
authors = ["James Kominick <james@kominick.com>"]
description = "Generic cache implementations and simplified function memoization"
repository = "https://github.com/jaemk/cached"
documentation = "https://docs.rs/cached"
readme = "README.md"
categories = ["caching", "data-structures"]
keywords = ["cache", "memoize", "lru", "redis", "disk"]
license = "MIT"
edition = "2024"
rust-version = "1.92"
# Internal dev tooling and process docs that should not ship in the published crate.
exclude = [".agents/", ".claude/", ".github/", "bin/", "docs/dev/", "specs/", "local/", ".cursorrules", "AGENTS.md", "CLAUDE.md", "Makefile"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[features]
default = ["proc_macro", "ahash", "time_stores"]
proc_macro = ["dep:cached_proc_macro", "dep:cached_proc_macro_types"]
ahash = ["dep:ahash", "hashbrown/default"]
# Runtime-agnostic async trait definitions (ConcurrentCachedAsync, CachedGetOrSetAsync,
# etc.) without the async-lock dependency. Enabled by `async`. Kept public so callers
# can access the async trait surface without pulling in async-lock (see design/0016).
async_core = []
async = ["async_core", "dep:async-lock"]
# NOTE: there is deliberately no public `serde` feature. The MessagePack codec
# (`serde` + `rmp-serde`) is an implementation detail of the IO stores, which pull it
# directly below. Nothing in the public surface is gated on it -- `SerializeCached` and
# `SerializeCachedAsync` are ungated traits -- so a standalone `serde` feature only ever
# added two dependencies to a consumer's graph and no API.
redis_store = ["dep:serde", "dep:rmp-serde", "dep:redis", "dep:r2d2", "dep:serde_json"]
# Runtime axis: the redis async capabilities below (`redis_connection_manager`,
# `redis_async_cache`) express only the redis capability and are runtime-agnostic;
# the async runtime is chosen separately via `redis_tokio` (or its TLS variants) or
# `redis_smol`. A capability feature must be combined with a runtime feature to
# yield a usable `AsyncRedisCache`; enabling it alone leaves you without a runtime
# (redis-rs has nothing to drive the connection and will fail to build/connect).
#
# `redis/connection-manager` depends only on `redis/aio` (not `tokio-comp`), so the
# auto-reconnecting connection manager composes with either runtime -- a `redis_smol`
# user can enable it. Note it is now additive: it makes the manager *available* as a
# per-cache builder opt-in (`.connection_manager(true)`) and no longer swaps every
# `AsyncRedisCache` from multiplexed to manager. It pulls in `redis_store` + `async`
# (like `redis_async_cache`) so the async store compiles; a runtime feature
# (`redis_tokio*` / `redis_smol*`) is still required to actually connect.
redis_connection_manager = ["redis_store", "async", "redis/connection-manager"]
redis_smol = [
  "redis_store",
  "async",
  "redis/smol-comp",
]
redis_smol_native_tls = ["redis_smol", "redis/smol-native-tls-comp"]
redis_smol_rustls = ["redis_smol", "redis/smol-rustls-comp"]
redis_tokio = [
  "redis_store",
  "async",
  "redis/tokio-comp",
]
redis_tokio_native_tls = ["redis_tokio", "redis/tokio-native-tls-comp"]
redis_tokio_rustls = ["redis_tokio", "redis/tokio-rustls-comp"]
# `redis/cache-aio` (client-side caching) depends only on `redis/aio` (not
# `tokio-comp`) in redis 1.x, so it is runtime-agnostic like the connection manager.
# It therefore no longer hard-wires tokio: pair it with a runtime feature
# (`redis_tokio*` or `redis_smol*`). It keeps `redis_store`/`async` so the async
# store compiles, but still needs a runtime feature to actually connect.
redis_async_cache = ["redis_store", "async", "redis/cache-aio"]
# `blocking` (and its async-channel/futures-lite deps) is only used to offload
# the synchronous redb operations off the async executor, so it is pulled by
# `redb_store` rather than `async`. Redis-only and in-memory async builds do not
# pay for it. The `blocking::unblock` call sites are `#[cfg(feature = "async")]`,
# so a sync-only `redb_store` build enables the dependency without invoking it.
redb_store = ["dep:serde", "dep:rmp-serde", "dep:redb", "dep:directories", "dep:blocking"]
time_stores = []

[dependencies.cached_proc_macro]
version = "3.0.0"
path = "cached_proc_macro"
optional = true

[dependencies.cached_proc_macro_types]
version = "3.0.0"
path = "cached_proc_macro_types"
optional = true

[dependencies.hashbrown]
version = "0.17"
default-features = false
features = ["inline-more"]

[dependencies.parking_lot]
version = "0.12.5"

[dependencies.thiserror]
version = "2"

[dependencies.ahash]
version = "0.8"
default-features = false
optional = true

# On non-wasm targets, enable runtime-rng so RandomState seeds from the OS RNG
# (closes a hash-flood DoS gap). On wasm32-unknown-unknown, getrandom has no
# usable backend unless the caller wires up wasm_js, so we leave runtime-rng
# out there. Cargo additively merges features across target-gated and
# unconditional sections for the same dep, so non-wasm gets runtime-rng while
# wasm falls back to compile-time seeding only.
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.ahash]
version = "0.8"
default-features = false
features = ["runtime-rng"]
optional = true


[dependencies.redis]
version = "1.0"
features = ["r2d2"]
optional = true

[dependencies.redb]
version = "4"
optional = true

[dependencies.rmp-serde]
version = "1.1"
optional = true

[dependencies.directories]
version = "6.0"
optional = true

[dependencies.r2d2]
version = "0.8"
optional = true

[dependencies.serde]
version = "1.0"
features = ["derive"]
optional = true

[dependencies.serde_json]
version = "1"
optional = true

[dependencies.async-lock]
version = "3"
optional = true

[dependencies.blocking]
version = "1"
optional = true

[dependencies.web-time]
version = "^1.1.0"

[dev-dependencies]
googletest = "0.14"
tempfile = "3.10.1"
trybuild = "1"
criterion = "0.8"
futures = "0.3"

[dev-dependencies.tokio]
version = "1"
features = ["macros", "time", "sync", "parking_lot", "rt", "rt-multi-thread"]

[dev-dependencies.async-std]
version = "1.6"
features = ["attributes"]

[dev-dependencies.smartstring]
version = "1"

[dev-dependencies.serial_test]
version = "3"

# Only for `examples/moka_custom_store.rs`, which demonstrates wrapping a
# third-party concurrent cache in `ConcurrentCached`. Deliberately a
# dev-dependency: nothing in `src/` references moka.
[dev-dependencies.moka]
version = "0.12"
features = ["sync"]

[workspace]
members = ["cached_proc_macro", "examples/wasm"]

[[example]]
name = "redis"
required-features = ["redis_store", "proc_macro"]

[[example]]
name = "redis-async-tokio"
required-features = ["redis_tokio_native_tls", "proc_macro"]

[[example]]
name = "redis-async-async-std"
required-features = ["redis_smol_native_tls", "proc_macro"]

[[example]]
name = "tokio"
required-features = ["async", "proc_macro"]

[[example]]
name = "async_std"
required-features = ["async", "proc_macro"]

[[example]]
name = "expires_per_key"
required-features = ["proc_macro"]

[[example]]
name = "expiring_sized_cache"
required-features = ["time_stores"]

[[example]]
name = "basic"
required-features = ["time_stores", "proc_macro"]

[[example]]
name = "kitchen_sink"
required-features = ["time_stores", "proc_macro"]

[[example]]
name = "resilience"
required-features = ["time_stores", "proc_macro"]

[[example]]
name = "stale_while_revalidate"
required-features = ["time_stores", "proc_macro", "async"]

[[example]]
name = "refresh_before_expiry"
required-features = ["time_stores", "proc_macro", "async"]

[[example]]
name = "disk"
required-features = ["redb_store", "proc_macro"]

[[example]]
name = "disk_async"
required-features = ["redb_store", "async", "proc_macro"]

[[example]]
name = "redis-client-side-cache-tokio"
required-features = ["redis_async_cache", "redis_tokio_native_tls", "proc_macro"]

[[example]]
name = "sharded"
required-features = ["time_stores", "proc_macro"]

[[example]]
name = "sharded_expiring"
required-features = ["proc_macro"]

[[example]]
name = "struct_method"
required-features = ["proc_macro"]

[[example]]
name = "moka_custom_store"
required-features = ["proc_macro"]

[[bench]]
name = "cache_benches"
harness = false
required-features = ["time_stores", "proc_macro"]