use std::sync::atomic::{AtomicU64, Ordering};
use cached::macros::{cached, once};
use cached::stores::ExpiringCache;
use cached::time::{Duration, Instant};
use cached::{Cached, Expires, ExpiringLruCache};
static CALL_N: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Debug)]
struct MyValue {
data: String,
expires_at: Instant,
}
impl Expires for MyValue {
fn is_expired(&self) -> bool {
Instant::now() >= self.expires_at
}
}
#[cached(expires = true, key = "u64", convert = "{ user_id }")]
fn fetch_token(user_id: u64, expiry_offset_ms: u64) -> MyValue {
println!(" -> [fetch_token] generating new token for user {user_id}...");
let n = CALL_N.fetch_add(1, Ordering::Relaxed);
MyValue {
data: format!("token-{user_id}-{n}"),
expires_at: Instant::now() + Duration::from_millis(expiry_offset_ms),
}
}
#[once(expires = true)]
fn get_session_token(expiry_offset_ms: u64) -> MyValue {
println!(" -> [get_session_token] generating new token...");
let n = CALL_N.fetch_add(1, Ordering::Relaxed);
MyValue {
data: format!("session-token-{n}"),
expires_at: Instant::now() + Duration::from_millis(expiry_offset_ms),
}
}
fn main() {
let now = Instant::now();
println!("--- 1. ExpiringLruCache (Size-bounded) ---");
let mut lru_cache = ExpiringLruCache::with_size(10);
let quick_expiry = MyValue {
data: "Short-lived LRU response".to_string(),
expires_at: now + Duration::from_millis(500),
};
lru_cache.cache_set("short", quick_expiry);
let long_expiry = MyValue {
data: "Long-lived LRU response".to_string(),
expires_at: now + Duration::from_secs(10),
};
lru_cache.cache_set("long", long_expiry);
println!("Immediately after insertion into ExpiringLruCache:");
if let Some(val) = lru_cache.cache_get(&"short") {
println!(" 'short' exists: '{}'", val.data);
}
if let Some(val) = lru_cache.cache_get(&"long") {
println!(" 'long' exists: '{}'", val.data);
}
println!("\nWaiting 1 second...");
std::thread::sleep(Duration::from_secs(1));
println!("After waiting 1 second:");
match lru_cache.cache_get(&"short") {
Some(val) => println!(" 'short' exists: '{}'", val.data),
None => println!(" 'short' has expired and was removed!"),
}
match lru_cache.cache_get(&"long") {
Some(val) => println!(" 'long' exists: '{}' (still active)", val.data),
None => println!(" 'long' expired!"),
}
println!("\n--- 2. ExpiringCache (Size-unbounded) ---");
let mut expiring_cache = ExpiringCache::new();
let quick_expiry = MyValue {
data: "Short-lived response".to_string(),
expires_at: Instant::now() + Duration::from_millis(500),
};
expiring_cache.cache_set("short", quick_expiry);
let long_expiry = MyValue {
data: "Long-lived response".to_string(),
expires_at: Instant::now() + Duration::from_secs(10),
};
expiring_cache.cache_set("long", long_expiry);
println!("Immediately after insertion into ExpiringCache:");
if let Some(val) = expiring_cache.cache_get(&"short") {
println!(" 'short' exists: '{}'", val.data);
}
if let Some(val) = expiring_cache.cache_get(&"long") {
println!(" 'long' exists: '{}'", val.data);
}
println!("\nWaiting 1 second...");
std::thread::sleep(Duration::from_secs(1));
println!("After waiting 1 second:");
match expiring_cache.cache_get(&"short") {
Some(val) => println!(" 'short' exists: '{}'", val.data),
None => println!(" 'short' has expired and was removed!"),
}
match expiring_cache.cache_get(&"long") {
Some(val) => println!(" 'long' exists: '{}' (still active)", val.data),
None => println!(" 'long' expired!"),
}
println!("\n--- 3. #[cached(expires = true)] Macro (keyed) ---");
println!("First call for user 1 (expires in 500ms):");
let u1_t1 = fetch_token(1, 500);
println!(" Returned: '{}'", u1_t1.data);
println!("First call for user 2 (expires in 10s):");
let u2_t1 = fetch_token(2, 10_000);
println!(" Returned: '{}'", u2_t1.data);
println!("\nSecond call for user 1 (cache hit):");
let u1_t2 = fetch_token(1, 500);
println!(" Returned: '{}' (same token)", u1_t2.data);
println!("\nWaiting 1 second...");
std::thread::sleep(Duration::from_secs(1));
println!("\nAfter 1 second:");
let u1_t3 = fetch_token(1, 500);
println!(" user 1: '{}' (re-evaluated — was expired)", u1_t3.data);
let u2_t2 = fetch_token(2, 10_000);
println!(" user 2: '{}' (cache hit — still live)", u2_t2.data);
println!("\n--- 4. #[once(expires = true)] Macro (single value) ---");
println!("First call (creating token that expires in 500ms):");
let t1 = get_session_token(500);
println!(" Returned: '{}'", t1.data);
println!("\nSecond call (immediately after):");
let t2 = get_session_token(500);
println!(" Returned: '{}' (cache hit)", t2.data);
println!("\nWaiting 1 second...");
std::thread::sleep(Duration::from_secs(1));
println!("Third call (after token has expired):");
let t3 = get_session_token(500);
println!(" Returned: '{}' (cache miss, re-evaluated)", t3.data);
}