Skip to main content

cachekit/
lib.rs

1//! CacheKit — caching for Rust.
2//!
3//! Supports cachekit.io SaaS, Redis, Memcached, local File, and Cloudflare
4//! Workers backends. Zero-knowledge encryption via AES-256-GCM with HKDF key
5//! derivation.
6
7// Production code lints — these only fire in src/, not tests/
8#![warn(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
9#![warn(missing_docs)]
10
11// Mutually exclusive feature guards
12#[cfg(all(feature = "workers", feature = "redis"))]
13compile_error!(
14    "features `workers` and `redis` are mutually exclusive — Workers runtime cannot use fred"
15);
16
17#[cfg(all(feature = "workers", feature = "l1"))]
18compile_error!("features `workers` and `l1` are mutually exclusive — moka requires std threads unavailable in wasm32");
19
20#[cfg(all(feature = "workers", feature = "reliability"))]
21compile_error!("features `workers` and `reliability` are mutually exclusive — retry/breaker timers need tokio `time`, unavailable in wasm32");
22
23#[cfg(all(feature = "workers", feature = "memcached"))]
24compile_error!("features `workers` and `memcached` are mutually exclusive — Workers runtime has no TCP sockets");
25
26#[cfg(all(feature = "workers", feature = "file"))]
27compile_error!(
28    "features `workers` and `file` are mutually exclusive — Workers runtime has no filesystem"
29);
30
31/// Pluggable cache backend trait and implementations (CachekitIO, Redis,
32/// Memcached, File, Workers).
33pub mod backend;
34/// High-level cache client with dual-layer (L1/L2) support.
35pub mod client;
36/// Configuration types and environment variable parsing.
37pub mod config;
38/// Error types for cache operations and backend communication.
39pub mod error;
40/// Cold-miss single-flight: dedup concurrent fills of the same key.
41pub mod flight;
42/// Interop mode (interop/v1): cross-SDK cache keys and plain-MessagePack values.
43pub mod interop;
44/// L1 cache hit-rate metrics for CachekitIO request headers.
45pub mod metrics;
46/// Serialization and deserialization of cached values via MessagePack.
47pub mod serializer;
48/// SDK session tracking (session ID and start timestamp).
49pub mod session;
50/// SSRF-safe URL validation for CachekitIO endpoints.
51pub mod url_validator;
52
53/// Intent-based cache presets (`CacheKit::minimal`, `::production`, `::encrypted`, `::io`).
54mod intents;
55
56/// Client-side AES-256-GCM encryption with HKDF key derivation.
57#[cfg(feature = "encryption")]
58pub mod encryption;
59
60/// In-process L1 cache backed by [`moka`] with per-entry TTL.
61#[cfg(feature = "l1")]
62pub mod l1;
63
64/// Reliability tier: retry with backoff + jitter, circuit breaker.
65#[cfg(all(feature = "reliability", not(target_arch = "wasm32")))]
66pub mod reliability;
67
68// Re-exports
69pub use client::{CacheKit, CacheKitBuilder, SharedBackend, SwrRead, SwrToken};
70pub use config::CachekitConfig;
71pub use error::{BackendError, BackendErrorKind, CachekitError};
72
73#[cfg(feature = "encryption")]
74pub use client::SecureCache;
75#[cfg(feature = "encryption")]
76pub use encryption::EncryptionLayer;
77
78#[cfg(feature = "macros")]
79pub use cachekit_macros::cachekit;
80
81pub use flight::SingleFlight;
82
83#[cfg(all(feature = "reliability", not(target_arch = "wasm32")))]
84pub use reliability::{BackpressureConfig, CircuitBreakerConfig, ReliabilityConfig, RetryConfig};
85
86// ── Shared jitter source ─────────────────────────────────────────────────────
87
88/// Uniform random in `[0, 1)`. uuid v4 is the crate's existing entropy source
89/// (getrandom-backed); jitter needs decorrelation across clients, not crypto
90/// quality — 53 bits is plenty. Used by retry backoff (`reliability`) and the
91/// L1 SWR freshness threshold at entry insertion (`l1`).
92#[cfg(any(
93    feature = "l1",
94    all(feature = "reliability", not(target_arch = "wasm32"))
95))]
96pub(crate) fn random_unit() -> f64 {
97    let bits = uuid::Uuid::new_v4().as_u128() & ((1u128 << 53) - 1);
98    (bits as f64) / ((1u64 << 53) as f64)
99}
100
101// ── SWR background-refresh spawn (macro plumbing) ───────────────────────────
102
103/// Spawn a stale-while-revalidate background refresh onto the ambient tokio
104/// runtime. Macro plumbing for `#[cachekit]` — not public API.
105///
106/// Without a tokio runtime on the current thread the refresh is skipped: the
107/// caller has already been served the stale value, and a later stale read
108/// simply retries. Panicking here would turn a cache optimisation into an
109/// availability bug on non-tokio executors.
110#[cfg(all(feature = "l1", not(feature = "unsync"), not(target_arch = "wasm32")))]
111#[doc(hidden)]
112pub fn __swr_spawn<F>(fut: F)
113where
114    F: std::future::Future<Output = ()> + Send + 'static,
115{
116    if let Ok(handle) = tokio::runtime::Handle::try_current() {
117        drop(handle.spawn(fut));
118    }
119}
120
121/// No-op variant: under `unsync`, on wasm32, or without the `l1` feature the
122/// client never classifies a hit as stale (`SwrRead::Stale` is unreachable),
123/// so the refresh future handed here is dead code by construction. The stub
124/// exists so `#[cachekit]`-generated code compiles under every configuration.
125#[cfg(not(all(feature = "l1", not(feature = "unsync"), not(target_arch = "wasm32"))))]
126#[doc(hidden)]
127pub fn __swr_spawn<F>(_fut: F)
128where
129    F: std::future::Future<Output = ()> + 'static,
130{
131}
132
133/// Convenient glob import for the most common types.
134pub mod prelude {
135    pub use crate::{
136        BackendError, BackendErrorKind, CacheKit, CacheKitBuilder, CachekitConfig, CachekitError,
137        SwrRead, SwrToken,
138    };
139
140    #[cfg(feature = "encryption")]
141    pub use crate::{EncryptionLayer, SecureCache};
142
143    #[cfg(feature = "macros")]
144    pub use crate::cachekit;
145}