use std::future::Future;
pub mod memory;
pub mod noop;
pub mod r2;
pub mod tiered;
pub use memory::MemoryCache;
pub use noop::NoopCache;
pub use r2::R2Cache;
pub use tiered::TieredCache;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CacheEntry {
pub data: Vec<u8>,
pub content_type: String,
pub created_at: i64,
}
pub trait Cache: Send + Sync {
fn get(&self, key: &str) -> impl Future<Output = Option<CacheEntry>> + Send;
fn put(&self, key: &str, entry: CacheEntry) -> impl Future<Output = ()> + Send;
fn delete(&self, key: &str) -> impl Future<Output = bool> + Send;
fn clear(&self) -> impl Future<Output = ()> + Send;
fn size(&self) -> impl Future<Output = usize> + Send;
}
pub fn compute_cache_key(image_path: &str, transform_string: &str, format: &str) -> String {
format!("{image_path}|{transform_string}|{format}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_cache_key_is_deterministic() {
let k1 = compute_cache_key("/img/photo.jpg", "w=200,h=100", "webp");
let k2 = compute_cache_key("/img/photo.jpg", "w=200,h=100", "webp");
assert_eq!(k1, k2);
}
#[test]
fn compute_cache_key_differs_for_different_paths() {
let k1 = compute_cache_key("/a.jpg", "w=100", "png");
let k2 = compute_cache_key("/b.jpg", "w=100", "png");
assert_ne!(k1, k2);
}
#[test]
fn compute_cache_key_differs_for_different_transforms() {
let k1 = compute_cache_key("/a.jpg", "w=100", "png");
let k2 = compute_cache_key("/a.jpg", "w=200", "png");
assert_ne!(k1, k2);
}
#[test]
fn compute_cache_key_differs_for_different_formats() {
let k1 = compute_cache_key("/a.jpg", "w=100", "png");
let k2 = compute_cache_key("/a.jpg", "w=100", "webp");
assert_ne!(k1, k2);
}
#[test]
fn compute_cache_key_includes_all_components_separated_by_pipe() {
assert_eq!(
compute_cache_key("path", "transforms", "fmt"),
"path|transforms|fmt"
);
}
}