1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! A trait-based, deduplicating, in-memory cache for Rust.
//!
//! One data copy per unique identity, even when the same record is fetched
//! through different query arguments.
//!
//! ```rust
//! use std::time::Duration;
//! use imc::{ImcCacheable, CacheStrategy, through_imc};
//!
//! #[derive(Clone, Debug, PartialEq)]
//! struct User { id: u32, name: String }
//!
//! impl ImcCacheable for User {
//! type Id = u32;
//! type Key = String;
//! fn cache_id(&self) -> u32 { self.id }
//! fn cache_strategy() -> CacheStrategy { CacheStrategy::Lru }
//! fn cache_ttl() -> Option<Duration> { Some(Duration::from_secs(300)) }
//! fn cache_max_size() -> usize { 10_000 }
//! }
//!
//! let user: User = through_imc(42u32, || User { id: 42, name: "Alice".into() });
//! let same: User = through_imc("alice@example.com", || User { id: 42, name: "Alice".into() });
//! assert_eq!(user, same);
//! ```
//!
//! ---
//!
//! # Feature flags
//!
//! | Feature | Description |
//! |---------|-------------|
//! | *(none)* | Core caching: [`through_imc`], dedup, eviction, TTL |
//! | `async` | Enables [`through_imc_async`] / [`through_imc_keyed_async`] (runtime-agnostic) |
//! | `tokio` | Implies `async` + makes [`CacheWorker`] use `tokio::task::spawn_blocking` |
//! | `invalidation-redis` | Cross-process cache invalidation via Redis pub/sub |
//! | `critical` | Critical-key broadcast via Redis pub/sub. Implies `invalidation-redis`. Requires `#[derive(CriticalKey)]` on key enums used with [`through_imc_keyed`] |
//! | `logging` | Structured tracing events via [`log_event!`] macro |
//! | `metrics-prometheus` | Prometheus counters/gauges + HTTP `/metrics` endpoint |
//!
//! ---
//!
//! # Quick start
//!
//! 1. **Add `imc` to `Cargo.toml`**
//!
//! ```toml
//! [dependencies]
//! imc = "0.1"
//! ```
//!
//! 2. **Implement [`ImcCacheable`] on your type**
//!
//! ```rust
//! use std::time::Duration;
//! use imc::{ImcCacheable, CacheStrategy};
//!
//! #[derive(Clone)]
//! struct User { id: u32, name: String }
//!
//! impl ImcCacheable for User {
//! type Id = u32;
//! type Key = String;
//!
//! fn cache_id(&self) -> u32 { self.id }
//!
//! fn cache_strategy() -> CacheStrategy { CacheStrategy::Lru }
//! fn cache_ttl() -> Option<Duration> { Some(Duration::from_secs(300)) }
//! fn cache_max_size() -> usize { 10_000 }
//! }
//! ```
//!
//! 3. **Cache expensive operations**
//!
//! ```rust
//! # use std::time::Duration;
//! # use imc::{ImcCacheable, CacheStrategy, through_imc};
//! # #[derive(Clone, PartialEq, Debug)]
//! # struct User { id: u32, name: String }
//! # impl ImcCacheable for User {
//! # type Id = u32; type Key = String;
//! # fn cache_id(&self) -> u32 { self.id }
//! # fn cache_strategy() -> CacheStrategy { CacheStrategy::Lru }
//! # fn cache_ttl() -> Option<Duration> { Some(Duration::from_secs(300)) }
//! # fn cache_max_size() -> usize { 10_000 }
//! # }
//! let user: User = through_imc(42u32, || fetch_user(42));
//! let same: User = through_imc("alice@example.com", || fetch_user_by_email("alice"));
//! assert_eq!(user.id, same.id);
//! # fn fetch_user(id: u32) -> User { User { id, name: "Alice".into() } }
//! # fn fetch_user_by_email(_: &str) -> User { User { id: 42, name: "Alice".into() } }
//! ```
//!
//! # Re-exports
//!
//! The [`CriticalKey`] derive macro is provided by
//! the `imc-derive` crate and re-exported here for convenience:
//!
//! ```rust,ignore
//! use imc::CriticalKey;
//!
//! #[derive(Hash, Clone, CriticalKey)]
//! enum UserKey { ById(i32), ByEmail(String) }
//! ```
pub use CriticalKey;
pub
pub
pub
pub
pub
pub use ;
pub use CriticalKey;
pub use ;
pub use ;
pub use ;
// ---------------------------------------------------------------------------
// Library lifecycle
// ---------------------------------------------------------------------------
/// Entry point for the imc cache system.
///
/// Call [`Imc::init`] or [`Imc::start`] once at application startup to
/// explicitly initialise the global cache store and optionally spawn the
/// background maintenance worker.
///
/// The cache store is a process-global singleton, so any call to
/// [`through_imc`], [`imc_remove`], etc. works automatically — even without
/// calling `init`. However, calling `init` or `start` makes the lifecycle
/// explicit and gives you a chance to attach a [`CacheWorker`].
///
/// # Example
///
/// ```rust,ignore
/// use imc::Imc;
///
/// // Basic initialisation (no background worker):
/// Imc::init();
///
/// // Or with a background worker:
/// let _worker = Imc::start(Default::default());
///
/// // Use the cache anywhere in the codebase:
/// let user = imc::through_imc(42u32, || fetch_user(42));
/// ```
;