use cache_kit::{
backend::InMemoryBackend,
error::Result,
observability::{CacheMetrics, TtlPolicy},
strategy::CacheStrategy,
CacheEntity, CacheExpander, CacheFeed, DataRepository,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Clone, Serialize, Deserialize, Debug)]
struct Product {
id: String,
name: String,
price: f64,
category: String,
}
impl CacheEntity for Product {
type Key = String;
fn cache_key(&self) -> Self::Key {
self.id.clone()
}
fn cache_prefix() -> &'static str {
"product"
}
}
struct ProductFeeder {
id: String,
product: Option<Product>,
}
impl CacheFeed<Product> for ProductFeeder {
fn entity_id(&mut self) -> String {
self.id.clone()
}
fn feed(&mut self, entity: Option<Product>) {
self.product = entity;
}
}
struct ProductRepository;
impl DataRepository<Product> for ProductRepository {
async fn fetch_by_id(&self, id: &String) -> Result<Option<Product>> {
println!(" [DB] Loading product: {}", id);
let product = match id.as_str() {
"prod_001" => Some(Product {
id: id.clone(),
name: "Laptop".to_string(),
price: 999.99,
category: "electronics".to_string(),
}),
"prod_002" => Some(Product {
id: id.clone(),
name: "Coffee Maker".to_string(),
price: 49.99,
category: "appliances".to_string(),
}),
_ => None,
};
Ok(product)
}
}
struct SimpleMetrics {
hits: std::sync::Arc<std::sync::atomic::AtomicUsize>,
misses: std::sync::Arc<std::sync::atomic::AtomicUsize>,
}
impl SimpleMetrics {
fn new() -> Self {
SimpleMetrics {
hits: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
misses: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
}
}
#[allow(dead_code)]
fn stats(&self) -> (usize, usize) {
let hits = self.hits.load(std::sync::atomic::Ordering::Relaxed);
let misses = self.misses.load(std::sync::atomic::Ordering::Relaxed);
(hits, misses)
}
}
impl CacheMetrics for SimpleMetrics {
fn record_hit(&self, key: &str, duration: Duration) {
self.hits.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
println!(" [METRIC] Cache HIT: {} ({:?})", key, duration);
}
fn record_miss(&self, key: &str, duration: Duration) {
self.misses
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
println!(" [METRIC] Cache MISS: {} ({:?})", key, duration);
}
fn record_error(&self, key: &str, error: &str) {
println!(" [METRIC] ERROR: {}: {}", key, error);
}
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Info)
.try_init()
.ok();
println!("\n=== Cache Kit - Advanced Builder Pattern ===\n");
println!("1. Setting up per-type TTL policy...");
let ttl_policy = TtlPolicy::PerType(|entity_type| match entity_type {
"product" => {
println!(" → Product TTL: 1 hour");
Duration::from_secs(3600)
}
_ => {
println!(" → Default TTL: 30 minutes");
Duration::from_secs(1800)
}
});
println!("2. Creating cache expander with custom configuration...\n");
let backend = InMemoryBackend::new();
let expander = CacheExpander::new(backend)
.with_metrics(Box::new(SimpleMetrics::new()))
.with_ttl_policy(ttl_policy);
let repository = ProductRepository;
println!("3. Executing cache operations:\n");
println!(" Request 1: prod_001 (cache miss)");
let mut feeder = ProductFeeder {
id: "prod_001".to_string(),
product: None,
};
expander
.with(&mut feeder, &repository, CacheStrategy::Refresh)
.await?;
if let Some(product) = &feeder.product {
println!(" ✓ Loaded: {} - ${:.2}", product.name, product.price);
}
println!();
println!(" Request 2: prod_001 (cache hit)");
let mut feeder = ProductFeeder {
id: "prod_001".to_string(),
product: None,
};
expander
.with(&mut feeder, &repository, CacheStrategy::Refresh)
.await?;
if let Some(product) = &feeder.product {
println!(
" ✓ Loaded from cache: {} - ${:.2}",
product.name, product.price
);
}
println!();
println!(" Request 3: prod_002 (cache miss)");
let mut feeder = ProductFeeder {
id: "prod_002".to_string(),
product: None,
};
expander
.with(&mut feeder, &repository, CacheStrategy::Refresh)
.await?;
if let Some(product) = &feeder.product {
println!(" ✓ Loaded: {} - ${:.2}", product.name, product.price);
}
println!();
println!(" Request 4: prod_001 with Invalidate strategy");
let mut feeder = ProductFeeder {
id: "prod_001".to_string(),
product: None,
};
expander
.with(&mut feeder, &repository, CacheStrategy::Invalidate)
.await?;
if let Some(product) = &feeder.product {
println!(
" ✓ Refreshed from database: {} - ${:.2}",
product.name, product.price
);
}
println!("\n4. Cache statistics:");
println!(" ✓ Framework successfully uses advanced features:");
println!(" - Custom metrics tracking");
println!(" - Per-type TTL policies");
println!(" - Multiple cache strategies");
println!("\n5. Per-Operation Configuration Examples:\n");
println!(" ℹ️ The configurations above (TTL policy, metrics) are set at");
println!(" setup time and apply to all operations. For per-operation");
println!(" overrides, use OperationConfig or the operation builder:\n");
println!(" Request 5a: Flash sale product with 1-minute TTL override");
let mut feeder = ProductFeeder {
id: "prod_001".to_string(),
product: None,
};
let config = cache_kit::OperationConfig::default().with_ttl(Duration::from_secs(60));
expander
.with_config(&mut feeder, &repository, CacheStrategy::Refresh, config)
.await?;
if let Some(product) = &feeder.product {
println!(
" ✓ Cached with 1-minute TTL: {} - ${:.2}",
product.name, product.price
);
}
println!();
println!(" Request 5b: Critical product lookup with retry");
let mut feeder = ProductFeeder {
id: "prod_002".to_string(),
product: None,
};
let config = cache_kit::OperationConfig::default()
.with_ttl(Duration::from_secs(300)) .with_retry(3);
expander
.with_config(&mut feeder, &repository, CacheStrategy::Refresh, config)
.await?;
if let Some(product) = &feeder.product {
println!(
" ✓ Loaded with retry protection: {} - ${:.2}",
product.name, product.price
);
}
println!();
println!(" ℹ️ Configuration Levels:\n");
println!(" Setup-time config (applies to all operations):");
println!(" - .with_metrics() → Observability");
println!(" - .with_ttl_policy() → Default TTL per entity type");
println!();
println!(" Per-operation config (overrides for specific calls):");
println!(" - .with_config() → Explicit OperationConfig");
println!(" └─ .with_ttl() → Override TTL for this call");
println!(" └─ .with_retry() → Add retry logic for this call");
println!("\n=== Example Complete ===\n");
Ok(())
}